public static async Task <T> WithRetriesAsync <T>(Func <Task <T> > action, int maxAttempts = 5, TimeSpan?retryInterval = null, CancellationToken cancellationToken = default, ILogger logger = null) { if (action == null) { throw new ArgumentNullException(nameof(action)); } int attempts = 1; var startTime = SystemClock.UtcNow; int currentBackoffTime = _defaultBackoffIntervals[0]; if (retryInterval != null) { currentBackoffTime = (int)retryInterval.Value.TotalMilliseconds; } do { if (attempts > 1 && logger != null && logger.IsEnabled(LogLevel.Information)) { logger.LogInformation("Retrying {Attempts} attempt after {Delay:g}...", attempts.ToOrdinal(), SystemClock.UtcNow.Subtract(startTime)); } try { return(await action().AnyContext()); } catch (Exception ex) { if (attempts >= maxAttempts) { throw; } if (logger != null && logger.IsEnabled(LogLevel.Error)) { logger.LogError(ex, "Retry error: {Message}", ex.Message); } await SystemClock.SleepSafeAsync(currentBackoffTime, cancellationToken).AnyContext(); } if (retryInterval == null) { currentBackoffTime = _defaultBackoffIntervals[Math.Min(attempts, _defaultBackoffIntervals.Length - 1)]; } attempts++; } while (attempts <= maxAttempts && !cancellationToken.IsCancellationRequested); throw new TaskCanceledException("Should not get here."); }