forked from App-vNext/Polly
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathReloadableComponent.cs
More file actions
113 lines (88 loc) · 3.76 KB
/
ReloadableComponent.cs
File metadata and controls
113 lines (88 loc) · 3.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
using Polly.Telemetry;
namespace Polly.Utils.Pipeline;
#pragma warning disable CA1031 // Do not catch general exception types
internal sealed class ReloadableComponent : PipelineComponent
{
public const string ReloadFailedEvent = "ReloadFailed";
public const string DisposeFailedEvent = "DisposeFailed";
public const string OnReloadEvent = "OnReload";
private readonly Func<Entry> _factory;
private ResilienceStrategyTelemetry _telemetry;
private CancellationTokenSource _tokenSource = null!;
private CancellationTokenRegistration _registration;
private List<CancellationToken> _reloadTokens;
public ReloadableComponent(Entry entry, Func<Entry> factory)
{
Component = entry.Component;
_reloadTokens = entry.ReloadTokens;
_factory = factory;
_telemetry = entry.Telemetry;
TryRegisterOnReload();
}
public PipelineComponent Component { get; private set; }
internal override ValueTask<Outcome<TResult>> ExecuteCore<TResult, TState>(
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
ResilienceContext context,
TState state)
{
return Component.ExecuteCore(callback, context, state);
}
public override ValueTask DisposeAsync()
{
DisposeRegistration();
return Component.DisposeAsync();
}
private void TryRegisterOnReload()
{
if (_reloadTokens.Count == 0)
{
return;
}
_tokenSource = CancellationTokenSource.CreateLinkedTokenSource([.. _reloadTokens]);
_registration = _tokenSource.Token.Register(() =>
{
var context = ResilienceContextPool.Shared.Get().Initialize<VoidResult>(isSynchronous: true);
var previousComponent = Component;
try
{
_telemetry.Report(new(ResilienceEventSeverity.Information, OnReloadEvent), context, new OnReloadArguments());
(Component, _reloadTokens, _telemetry) = _factory();
}
catch (Exception e)
{
_reloadTokens = [];
_telemetry.Report(new(ResilienceEventSeverity.Error, ReloadFailedEvent), context, Outcome.FromException(e), new ReloadFailedArguments(e));
ResilienceContextPool.Shared.Return(context);
}
DisposeRegistration();
TryRegisterOnReload();
_ = DisposeDiscardedComponentSafeAsync(previousComponent);
});
}
private async Task DisposeDiscardedComponentSafeAsync(PipelineComponent component)
{
var context = ResilienceContextPool.Shared.Get().Initialize<VoidResult>(isSynchronous: false);
try
{
await component.DisposeAsync().ConfigureAwait(false);
}
catch (Exception e)
{
_telemetry.Report(new(ResilienceEventSeverity.Error, DisposeFailedEvent), context, Outcome.FromException(e), new DisposedFailedArguments(e));
}
ResilienceContextPool.Shared.Return(context);
}
#pragma warning disable S2952 // Classes should "Dispose" of members from the classes' own "Dispose" methods
private void DisposeRegistration()
{
_registration.Dispose();
_tokenSource.Dispose();
}
#pragma warning restore S2952 // Classes should "Dispose" of members from the classes' own "Dispose" methods
internal record ReloadFailedArguments(Exception Exception);
internal record DisposedFailedArguments(Exception Exception);
#pragma warning disable S2094 // Classes should not be empty
internal record OnReloadArguments();
#pragma warning restore S2094 // Classes should not be empty
internal record Entry(PipelineComponent Component, List<CancellationToken> ReloadTokens, ResilienceStrategyTelemetry Telemetry);
}