Does the following line have to be disposed? If so, how?
var resetSignal = new CancellationTokenSource(sleepDuration.Add(TimeSpan.FromSeconds(-1)));
Code
internal static class Retry
{
private const string SleepDurationKey = "Broken";
#region Synchronous
public static void Do(Action action, TimeSpan retryInterval, TimeSpan durationOfBreak, int retryCount = 3)
{
_ = Do<object?>(() =>
{
action();
return null;
}, retryInterval, durationOfBreak, retryCount);
}
public static T Do<T>(Func<T> action, TimeSpan retryInterval, TimeSpan durationOfBreak, int retryCount = 3)
{
var circuitBreakerPolicy = CircuitBreakerPolicy();
var retryPolicy = RetryPolicy(circuitBreakerPolicy);
var policyResult = Policy
.Wrap(retryPolicy, circuitBreakerPolicy)
.ExecuteAndCapture(action);
if (policyResult.Outcome == OutcomeType.Failure)
{
throw policyResult.FinalException;
}
return policyResult.Result;
RetryPolicy RetryPolicy(ICircuitBreakerPolicy asyncPolicy)
{
return Policy
.Handle<Exception>()
.Or<BrokenCircuitException>()
.WaitAndRetryForever((_, ctx) =>
{
if (!ctx.ContainsKey(SleepDurationKey))
{
return retryInterval;
}
var sleepDuration = (TimeSpan)ctx[SleepDurationKey];
var resetSignal = new CancellationTokenSource(sleepDuration.Add(TimeSpan.FromSeconds(-1)));
resetSignal.Token.Register(() => { ctx.Remove(SleepDurationKey); asyncPolicy.Reset(); });
return sleepDuration;
});
}
CircuitBreakerPolicy CircuitBreakerPolicy()
{
return Policy
.Handle<Exception>()
.CircuitBreaker(retryCount, durationOfBreak,
onBreak: (dr, ts, ctx) => ctx[SleepDurationKey] = ts,
onReset: (ctx) => { });
}
}
#endregion
#region Asynchronous
public static async Task DoAsync(Func<Task> action, TimeSpan retryInterval, TimeSpan durationOfBreak, int retryCount = 3)
{
_ = await DoAsync<object?>(async () =>
{
await action();
return null;
}, retryInterval, durationOfBreak, retryCount);
}
public static async Task<T> DoAsync<T>(Func<Task<T>> action, TimeSpan retryInterval, TimeSpan durationOfBreak, int retryCount = 3)
{
var circuitBreakerPolicy = CircuitBreakerPolicy();
var retryPolicy = RetryPolicy(circuitBreakerPolicy);
var policyResult = await Policy
.WrapAsync(retryPolicy, circuitBreakerPolicy)
.ExecuteAndCaptureAsync(action);
if (policyResult.Outcome == OutcomeType.Failure)
{
throw policyResult.FinalException;
}
return policyResult.Result;
AsyncRetryPolicy RetryPolicy(ICircuitBreakerPolicy asyncPolicy)
{
return Policy
.Handle<Exception>()
.Or<BrokenCircuitException>()
.WaitAndRetryForeverAsync((_, ctx) =>
{
if (!ctx.ContainsKey(SleepDurationKey))
{
return retryInterval;
}
var sleepDuration = (TimeSpan)ctx[SleepDurationKey];
var resetSignal = new CancellationTokenSource(sleepDuration.Add(TimeSpan.FromSeconds(-1)));
resetSignal.Token.Register(() => { ctx.Remove(SleepDurationKey); asyncPolicy.Reset(); });
return sleepDuration;
});
}
AsyncCircuitBreakerPolicy CircuitBreakerPolicy()
{
return Policy
.Handle<Exception>()
.CircuitBreakerAsync(retryCount, durationOfBreak,
onBreak: (dr, ts, ctx) => ctx[SleepDurationKey] = ts,
onReset: (ctx) => { });
}
}
#endregion
}
Probably. It is not super clear to me what resources the token source owns that needs to be disposed, or if it only needs to be disposed in some cases. But whenever there is any doubt about if anything needs to be disposed, dispose it.
One way would be do do it in the callback
I'm not sure if that is the best way to do it. In general you cannot assume that the token will ever be cancelled. But it seems like you are mostly using the token as a timer, so I'm guessing it should be ok.
But since you seem to be using the cancellation token source as a timer, are you sure that it would not be better to just use one of the timer classes?