Skip to content

Commit bc07c7c

Browse files
authored
Fix Name and InstanceName not being set for reloadable pipelines (#1555)
1 parent e1b49f2 commit bc07c7c

File tree

7 files changed

+101
-22
lines changed

7 files changed

+101
-22
lines changed

src/Polly.Core/Registry/RegistryPipelineComponentBuilder.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ public RegistryPipelineComponentBuilder(
3333
internal PipelineComponent CreateComponent()
3434
{
3535
var builder = CreateBuilder();
36-
var telemetry = new ResilienceStrategyTelemetry(
37-
new ResilienceTelemetrySource(_builderName, _instanceName, null),
38-
builder.Listener);
39-
4036
var component = builder.ComponentFactory();
4137

4238
if (builder.ReloadTokens.Count == 0)
@@ -45,13 +41,12 @@ internal PipelineComponent CreateComponent()
4541
}
4642

4743
return PipelineComponentFactory.CreateReloadable(
48-
new ReloadableComponent.Entry(component, builder.ReloadTokens),
44+
new ReloadableComponent.Entry(component, builder.ReloadTokens, builder.Telemetry),
4945
() =>
5046
{
5147
var builder = CreateBuilder();
52-
return new ReloadableComponent.Entry(builder.ComponentFactory(), builder.ReloadTokens);
53-
},
54-
telemetry);
48+
return new ReloadableComponent.Entry(builder.ComponentFactory(), builder.ReloadTokens, builder.Telemetry);
49+
});
5550
}
5651

5752
private Builder CreateBuilder()
@@ -62,13 +57,20 @@ private Builder CreateBuilder()
6257
builder.InstanceName = _instanceName;
6358
_configure(builder, context);
6459

60+
var telemetry = new ResilienceStrategyTelemetry(
61+
new ResilienceTelemetrySource(builder.Name, builder.InstanceName, null),
62+
builder.TelemetryListener);
63+
6564
return new(
6665
() => PipelineComponentFactory.WithDisposableCallbacks(
6766
builder.BuildPipelineComponent(),
6867
context.DisposeCallbacks),
6968
context.ReloadTokens,
70-
builder.TelemetryListener);
69+
telemetry);
7170
}
7271

73-
private record Builder(Func<PipelineComponent> ComponentFactory, List<CancellationToken> ReloadTokens, TelemetryListener? Listener);
72+
private record Builder(
73+
Func<PipelineComponent> ComponentFactory,
74+
List<CancellationToken> ReloadTokens,
75+
ResilienceStrategyTelemetry Telemetry);
7476
}

src/Polly.Core/Utils/Pipeline/PipelineComponentFactory.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,5 @@ public static PipelineComponent CreateComposite(
3131

3232
public static PipelineComponent CreateReloadable(
3333
ReloadableComponent.Entry initial,
34-
Func<ReloadableComponent.Entry> factory,
35-
ResilienceStrategyTelemetry telemetry) => new ReloadableComponent(initial, factory, telemetry);
34+
Func<ReloadableComponent.Entry> factory) => new ReloadableComponent(initial, factory);
3635
}

src/Polly.Core/Utils/Pipeline/ReloadableComponent.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,18 @@ internal sealed class ReloadableComponent : PipelineComponent
1313
public const string OnReloadEvent = "OnReload";
1414

1515
private readonly Func<Entry> _factory;
16-
private readonly ResilienceStrategyTelemetry _telemetry;
16+
private ResilienceStrategyTelemetry _telemetry;
1717
private CancellationTokenSource _tokenSource = null!;
1818
private CancellationTokenRegistration _registration;
1919
private List<CancellationToken> _reloadTokens;
2020

21-
public ReloadableComponent(Entry entry, Func<Entry> factory, ResilienceStrategyTelemetry telemetry)
21+
public ReloadableComponent(Entry entry, Func<Entry> factory)
2222
{
2323
Component = entry.Component;
2424

2525
_reloadTokens = entry.ReloadTokens;
2626
_factory = factory;
27-
_telemetry = telemetry;
27+
_telemetry = entry.Telemetry;
2828

2929
TryRegisterOnReload();
3030
}
@@ -61,7 +61,7 @@ private void TryRegisterOnReload()
6161
try
6262
{
6363
_telemetry.Report(new(ResilienceEventSeverity.Information, OnReloadEvent), context, new OnReloadArguments());
64-
(Component, _reloadTokens) = _factory();
64+
(Component, _reloadTokens, _telemetry) = _factory();
6565
}
6666
catch (Exception e)
6767
{
@@ -107,5 +107,5 @@ internal record DisposedFailedArguments(Exception Exception);
107107

108108
internal record OnReloadArguments();
109109

110-
internal record Entry(PipelineComponent Component, List<CancellationToken> ReloadTokens);
110+
internal record Entry(PipelineComponent Component, List<CancellationToken> ReloadTokens, ResilienceStrategyTelemetry Telemetry);
111111
}

test/Polly.Core.Tests/Utils/Pipeline/ReloadablePipelineComponentTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ public async Task ChangeTriggered_StrategyReloaded()
5454
[Fact]
5555
public async Task ChangeTriggered_EnsureOldStrategyDisposed()
5656
{
57+
var telemetry = TestUtilities.CreateResilienceTelemetry(_listener);
5758
var component = Substitute.For<PipelineComponent>();
58-
await using var sut = CreateSut(component, () => new(Substitute.For<PipelineComponent>(), new List<CancellationToken>()));
59+
await using var sut = CreateSut(component, () => new(Substitute.For<PipelineComponent>(), new List<CancellationToken>(), telemetry));
5960

6061
for (var i = 0; i < 10; i++)
6162
{
@@ -130,12 +131,11 @@ public async Task DisposeError_EnsureReported()
130131

131132
private ReloadableComponent CreateSut(PipelineComponent? initial = null, Func<ReloadableComponent.Entry>? factory = null)
132133
{
133-
factory ??= () => new ReloadableComponent.Entry(PipelineComponent.Empty, new List<CancellationToken>());
134+
factory ??= () => new ReloadableComponent.Entry(PipelineComponent.Empty, new List<CancellationToken>(), _telemetry);
134135

135136
return (ReloadableComponent)PipelineComponentFactory.CreateReloadable(
136-
new ReloadableComponent.Entry(initial ?? PipelineComponent.Empty, new List<CancellationToken> { _cancellationTokenSource.Token }),
137-
factory,
138-
_telemetry);
137+
new ReloadableComponent.Entry(initial ?? PipelineComponent.Empty, new List<CancellationToken> { _cancellationTokenSource.Token }, _telemetry),
138+
factory);
139139
}
140140

141141
public void Dispose() => _cancellationTokenSource.Dispose();

test/Polly.Extensions.Tests/DependencyInjection/PollyServiceCollectionExtensionTests.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Globalization;
22
using Microsoft.Extensions.DependencyInjection;
3+
using Microsoft.Extensions.Logging;
34
using Microsoft.Extensions.Logging.Abstractions;
45
using Microsoft.Extensions.Options;
56
using Polly.DependencyInjection;
@@ -271,6 +272,55 @@ public void AddResiliencePipelineRegistry_ConfigureCallback_Ok()
271272
provider.GetRequiredService<IOptions<ResiliencePipelineRegistryOptions<string>>>().Value.InstanceNameFormatter.Should().Be(formatter);
272273
}
273274

275+
[InlineData(true)]
276+
[InlineData(false)]
277+
[Theory]
278+
public void AddResiliencePipeline_CustomInstanceName_EnsureReported(bool usingBuilder)
279+
{
280+
// arrange
281+
using var loggerFactory = new FakeLoggerFactory();
282+
283+
var context = ResilienceContextPool.Shared.Get("my-operation-key");
284+
var services = new ServiceCollection();
285+
var listener = new FakeTelemetryListener();
286+
var registry = services
287+
.AddResiliencePipeline("my-pipeline", ConfigureBuilder)
288+
.Configure<TelemetryOptions>(options => options.TelemetryListeners.Add(listener))
289+
.AddSingleton((ILoggerFactory)loggerFactory)
290+
.BuildServiceProvider()
291+
.GetRequiredService<ResiliencePipelineRegistry<string>>();
292+
293+
var pipeline = usingBuilder ?
294+
registry.GetPipeline("my-pipeline") :
295+
registry.GetOrAddPipeline("my-pipeline", ConfigureBuilder);
296+
297+
// act
298+
pipeline.Execute(_ => { }, context);
299+
300+
// assert
301+
foreach (var ev in listener.Events)
302+
{
303+
ev.Source.PipelineInstanceName.Should().Be("my-instance");
304+
ev.Source.PipelineName.Should().Be("my-pipeline");
305+
}
306+
307+
var record = loggerFactory.FakeLogger.GetRecords(new EventId(0, "ResilienceEvent")).First();
308+
309+
record.Message.Should().Contain("my-pipeline/my-instance");
310+
311+
static void ConfigureBuilder(ResiliencePipelineBuilder builder)
312+
{
313+
builder.Name.Should().Be("my-pipeline");
314+
builder.InstanceName = "my-instance";
315+
builder.AddRetry(new()
316+
{
317+
ShouldHandle = _ => PredicateResult.True(),
318+
MaxRetryAttempts = 3,
319+
Delay = TimeSpan.Zero
320+
});
321+
}
322+
}
323+
274324
private void AddResiliencePipeline(string key, Action<StrategyBuilderContext>? onBuilding = null)
275325
{
276326
_services.AddResiliencePipeline(key, builder =>

test/Polly.Extensions.Tests/ReloadableResiliencePipelineTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using NSubstitute;
44
using Polly.DependencyInjection;
55
using Polly.Registry;
6+
using Polly.Telemetry;
67

78
namespace Polly.Extensions.Tests;
89

@@ -20,6 +21,7 @@ public void AddResiliencePipeline_EnsureReloadable(string? name)
2021
var reloadableConfig = new ReloadableConfiguration();
2122
reloadableConfig.Reload(new() { { "tag", "initial-tag" } });
2223
var builder = new ConfigurationBuilder().Add(reloadableConfig);
24+
var fakeListener = new FakeTelemetryListener();
2325

2426
var services = new ServiceCollection();
2527

@@ -32,8 +34,11 @@ public void AddResiliencePipeline_EnsureReloadable(string? name)
3234
services.Configure<ReloadableStrategyOptions>(name, builder.Build());
3335
}
3436

37+
services.Configure<TelemetryOptions>(options => options.TelemetryListeners.Add(fakeListener));
3538
services.AddResiliencePipeline("my-pipeline", (builder, context) =>
3639
{
40+
builder.InstanceName = "my-instance";
41+
3742
var options = context.GetOptions<ReloadableStrategyOptions>(name);
3843
context.EnableReloads<ReloadableStrategyOptions>(name);
3944

@@ -76,6 +81,11 @@ public void AddResiliencePipeline_EnsureReloadable(string? name)
7681
resList.Last().Received(1).Dispose();
7782
pipeline.Invoking(p => p.Execute(() => { })).Should().Throw<ObjectDisposedException>();
7883

84+
foreach (var ev in fakeListener.Events)
85+
{
86+
ev.Source.PipelineName.Should().Be("my-pipeline");
87+
ev.Source.PipelineInstanceName.Should().Be("my-instance");
88+
}
7989
}
8090

8191
public class ReloadableStrategy : ResilienceStrategy, IDisposable
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Microsoft.Extensions.Logging;
2+
3+
namespace Polly.TestUtils;
4+
5+
public sealed class FakeLoggerFactory : ILoggerFactory
6+
{
7+
public FakeLogger FakeLogger { get; } = new FakeLogger();
8+
9+
public void AddProvider(ILoggerProvider provider)
10+
{
11+
}
12+
13+
public ILogger CreateLogger(string categoryName) => FakeLogger;
14+
15+
public void Dispose()
16+
{
17+
}
18+
}

0 commit comments

Comments
 (0)