|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Microsoft.DotNet.Cli.Utils;
public static class ExponentialRetry
{
public static IEnumerable<TimeSpan> Intervals
{
get
{
yield return TimeSpan.FromSeconds(0); // first retry immediately
var seconds = 5;
while (true)
{
yield return TimeSpan.FromSeconds(seconds);
seconds *= 10;
}
}
}
public static IEnumerable<TimeSpan> TestingIntervals
{
get
{
while (true)
{
yield return TimeSpan.FromSeconds(0);
}
}
}
public static async Task<T> ExecuteAsyncWithRetry<T>(Func<Task<T>> action,
Func<T, bool> shouldStopRetry,
int maxRetryCount,
Func<IEnumerable<Task>> timer,
string taskDescription = "")
{
var count = 0;
foreach (var t in timer())
{
await t;
T? result = default;
count++;
result = await action();
if (shouldStopRetry(result))
{
return result;
}
if (count == maxRetryCount)
{
return result;
}
}
throw new Exception($"Timer for task {taskDescription} should not be exhausted.");
}
public static async Task<T> ExecuteWithRetry<T>(Func<T> action,
Func<T, bool> shouldStopRetry,
int maxRetryCount,
Func<IEnumerable<Task>> timer,
string taskDescription = "")
{
Func<Task<T>> asyncAction = () => Task.FromResult(action());
return await ExecuteAsyncWithRetry(asyncAction, shouldStopRetry, maxRetryCount, timer, taskDescription);
}
public static async Task<T> ExecuteWithRetryOnFailure<T>(Func<Task<T>> action,
int maxRetryCount = 3,
Func<IEnumerable<Task>>? timer = null)
{
timer = timer ?? (() => Timer(Intervals));
return await ExecuteAsyncWithRetry(action, result => result != null && !result.Equals(default), maxRetryCount, timer);
}
public static IEnumerable<Task> Timer(IEnumerable<TimeSpan> interval)
{
return interval.Select(Task.Delay);
}
}
|