public void WithCallTiming_NonNull() { CallTiming timing1 = CallTiming.FromExpiration(Expiration.None); CallTiming timing2 = CallTiming.FromDeadline(DateTime.UtcNow); CancellationToken token = new CancellationTokenSource().Token; var original = new CallSettings(token, null, timing1, null, null, null); var result = original.WithCallTiming(timing2); Assert.Same(timing2, result.Timing); Assert.Equal(token, result.CancellationToken); }
public void MergedWith_BothNonNull() { CallTiming timing1 = CallTiming.FromExpiration(Expiration.None); CallTiming timing2 = CallTiming.FromDeadline(DateTime.UtcNow); CancellationToken token = new CancellationTokenSource().Token; var settings1 = new CallSettings(token, null, timing1, null, null, null); var settings2 = new CallSettings(null, null, timing2, null, null, null); var merged = settings1.MergedWith(settings2); Assert.Equal(token, merged.CancellationToken); Assert.Same(timing2, merged.Timing); }
// 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); } }
public void CallSettings_Overrides() { string projectId = _fixture.ProjectId; string topicId = _fixture.CreateTopicId(); DateTime deadline = DateTime.MaxValue; CancellationToken cancellationToken = new CancellationTokenSource().Token; // Sample: CallSettings_Overrides // Create a default PublisherSettings, with customizations for CreateTopic RPCs: // * A custom "ClientVersion" header. // * A custom 5-second timeout Timing. // * No cancellation token. PublisherSettings publisherSettings = new PublisherSettings(); publisherSettings.CreateTopicSettings.Headers = new Metadata { { "ClientVersion", "1.0.0" } }; publisherSettings.CreateTopicSettings.Timing = CallTiming.FromTimeout(TimeSpan.FromSeconds(5)); publisherSettings.CreateTopicSettings.CancellationToken = CancellationToken.None; // Override the above Timing and CancellationToken in the client-wide CallSettings; // the Headers are not overridden. publisherSettings.CallSettings = new CallSettings { Timing = CallTiming.FromDeadline(deadline), CancellationToken = CancellationToken.None, }; // Create the client with the configured publisherSettings PublisherClient client = PublisherClient.Create(settings: publisherSettings); // Format topicName from the projectId and topicId. string topicName = PublisherClient.FormatTopicName(projectId, topicId); // Call CreateTopic(). Override only the CancellationToken, using a per-RPC-method CallSettings. // The CallSettings used during this RPC invocation is: // * A custom "ClientVersion" header. // * A Timing deadline of 'deadline' (*not* the overridden 5-second timeout). // * The CancellationToken 'cancellationToken' (*not* CancellationToken.None). Topic topic = client.CreateTopic(topicName, new CallSettings { CancellationToken = cancellationToken }); // End sample }
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); } } }