internal static async Task <TResponse> CallWithRetryAsync <TResponse>( this Func <Task <TResponse> > someMethod, CallSettings callSettings, IClock clock, IScheduler scheduler) { RetrySettings retrySettings = callSettings.Timing?.Retry; if (retrySettings == null) { return(await someMethod().ConfigureAwait(false)); } DateTime?overallDeadline = retrySettings.TotalExpiration.CalculateDeadline(clock); TimeSpan retryDelay = retrySettings.RetryBackoff.Delay; while (true) { try { return(await someMethod().ConfigureAwait(false)); } catch (RpcException e) when(retrySettings.RetryFilter(e)) { TimeSpan actualDelay = retrySettings.DelayJitter.GetDelay(retryDelay); DateTime expectedRetryTime = clock.GetCurrentDateTimeUtc() + actualDelay; if (expectedRetryTime > overallDeadline) { throw; } await scheduler.Delay(actualDelay, callSettings.CancellationToken.GetValueOrDefault()).ConfigureAwait(false); retryDelay = retrySettings.RetryBackoff.NextDelay(retryDelay); } } }
// TODO: This is a modified/simplified version of ApiCallRetryExtensions.WithRetry from Google.Api.Gax.Grpc. Can we combine them somehow? internal static async Task RetryOperationUntilCompleted( Func <Task <bool> > fn, IClock clock, IScheduler scheduler, CallSettings callSettings, RetrySettings retrySettings) { DateTime?overallDeadline = retrySettings.TotalExpiration.CalculateDeadline(clock); TimeSpan retryDelay = retrySettings.RetryBackoff.Delay; TimeSpan callTimeout = retrySettings.TimeoutBackoff.Delay; while (true) { DateTime attemptDeadline = clock.GetCurrentDateTimeUtc() + callTimeout; // Note: this handles a null total deadline due to "<" returning false if overallDeadline is null. DateTime combinedDeadline = overallDeadline < attemptDeadline ? overallDeadline.Value : attemptDeadline; CallSettings attemptCallSettings = callSettings.WithCallTiming(CallTiming.FromDeadline(combinedDeadline)); TimeSpan actualDelay = retrySettings.DelayJitter.GetDelay(retryDelay); DateTime expectedRetryTime; try { bool isResponseOk = await fn().ConfigureAwait(false); if (isResponseOk) { return; } expectedRetryTime = clock.GetCurrentDateTimeUtc() + actualDelay; if (expectedRetryTime > overallDeadline) { // TODO: Can we get this string from somewhere? throw new RpcException(new Status(StatusCode.DeadlineExceeded, "Deadline Exceeded")); } } catch (RpcException e) when(retrySettings.RetryFilter(e)) { expectedRetryTime = clock.GetCurrentDateTimeUtc() + actualDelay; if (expectedRetryTime > overallDeadline) { throw; } } await scheduler.Delay(actualDelay, callSettings.CancellationToken.GetValueOrDefault()).ConfigureAwait(false); retryDelay = retrySettings.RetryBackoff.NextDelay(retryDelay); callTimeout = retrySettings.TimeoutBackoff.NextDelay(callTimeout); } }
internal static async Task <TResponse> Retry <TRequest, TResponse>( Func <TRequest, CallSettings, Task <TResponse> > fn, TRequest request, CallSettings callSettings, IClock clock, IScheduler scheduler) { RetrySettings retrySettings = callSettings.Timing?.Retry; if (retrySettings == null) { return(await fn(request, callSettings).ConfigureAwait(false)); } DateTime?overallDeadline = retrySettings.TotalExpiration.CalculateDeadline(clock); TimeSpan retryDelay = retrySettings.RetryBackoff.Delay; TimeSpan callTimeout = retrySettings.TimeoutBackoff.Delay; while (true) { DateTime attemptDeadline = clock.GetCurrentDateTimeUtc() + callTimeout; // Note: this handles a null total deadline due to "<" returning false if overallDeadline is null. DateTime combinedDeadline = overallDeadline < attemptDeadline ? overallDeadline.Value : attemptDeadline; CallSettings attemptCallSettings = callSettings.WithCallTiming(CallTiming.FromDeadline(combinedDeadline)); try { return(await fn(request, attemptCallSettings).ConfigureAwait(false)); } catch (RpcException e) when(retrySettings.RetryFilter(e)) { TimeSpan actualDelay = retrySettings.DelayJitter.GetDelay(retryDelay); DateTime expectedRetryTime = clock.GetCurrentDateTimeUtc() + actualDelay; if (expectedRetryTime > overallDeadline) { throw; } await scheduler.Delay(actualDelay, callSettings.CancellationToken.GetValueOrDefault()).ConfigureAwait(false); retryDelay = retrySettings.RetryBackoff.NextDelay(retryDelay); callTimeout = retrySettings.TimeoutBackoff.NextDelay(callTimeout); } } }