An RPC simple expiration; or retry settings.
        /// <summary>
        /// Returns a new <see cref="CallSettings"/> with the given the expiration, merged with the (optional) original settings specified by <paramref name="settings"/>.
        /// </summary>
        /// <remarks>
        /// If <paramref name="settings"/> is non-null and has retry-based timing, the returned settings will have the same timing, but with the specified
        /// expiration. Otherwise, the returned settings will have a simple expiration.
        /// </remarks>
        /// <param name="settings">Existing settings. May be null, meaning there are currently no settings.</param>
        /// <param name="expiration">Expiration to use in the returned settings, possibly as part of a retry. Must not be null.</param>
        /// <returns></returns>
        public static CallSettings WithExpiration(this CallSettings settings, Expiration expiration)
        {
            GaxPreconditions.CheckNotNull(expiration, nameof(expiration));
            var retry  = settings?.Timing?.Retry?.WithTotalExpiration(expiration);
            var timing = retry == null?CallTiming.FromExpiration(expiration) : CallTiming.FromRetry(retry);

            return(settings.WithCallTiming(timing));
        }
 /// <summary>
 /// Returns a new <see cref="CallSettings"/> with the specified call timing,
 /// merged with the (optional) original settings specified by <paramref name="settings"/>.
 /// </summary>
 /// <param name="settings">Original settings. May be null, in which case the returned settings
 /// will only contain call timing.</param>
 /// <param name="timing">Call timing for the new call settings.
 /// This may be null, in which case any call timing in <paramref name="settings"/> are
 /// not present in the new call settings. If both this and <paramref name="settings"/> are null,
 /// the return value is null.</param>
 /// <returns>A new set of call settings, or null if both parameters are null.</returns>
 public static CallSettings WithCallTiming(
     this CallSettings settings,
     CallTiming timing) =>
     settings == null
         ? CallSettings.FromCallTiming(timing)
         : new CallSettings(settings.CancellationToken, settings.Credentials,
             timing, settings.HeaderMutation,
             settings.WriteOptions, settings.PropagationToken);
Beispiel #3
0
 /// <summary>
 /// Returns a new <see cref="CallSettings"/> with the specified call timing,
 /// merged with the (optional) original settings specified by <paramref name="settings"/>.
 /// </summary>
 /// <param name="settings">Original settings. May be null, in which case the returned settings
 /// will only contain call timing.</param>
 /// <param name="timing">Call timing for the new call settings.
 /// This may be null, in which case any call timing in <paramref name="settings"/> are
 /// not present in the new call settings. If both this and <paramref name="settings"/> are null,
 /// the return value is null.</param>
 /// <returns>A new set of call settings, or null if both parameters are null.</returns>
 public static CallSettings WithCallTiming(
     this CallSettings settings,
     CallTiming timing) =>
 settings == null
         ? CallSettings.FromCallTiming(timing)
         : new CallSettings(settings.CancellationToken, settings.Credentials,
                            timing, settings.HeaderMutation,
                            settings.WriteOptions, settings.PropagationToken);
Beispiel #4
0
 /// <summary>
 /// Constructs an instance with the specified settings.
 /// </summary>
 /// <param name="cancellationToken">Cancellation token that can be used for cancelling the call.</param>
 /// <param name="credentials">Credentials to use for the call.</param>
 /// <param name="timing"><see cref="CallTiming"/> to use, or null for default retry/expiration behavior.</param>
 /// <param name="headerMutation">Action to modify the headers to send at the beginning of the call.</param>
 /// <param name="writeOptions"><see cref="global::Grpc.Core.WriteOptions"/> that will be used for the call.</param>
 /// <param name="propagationToken"><see cref="ContextPropagationToken"/> for propagating settings from a parent call.</param>
 public CallSettings(
     CancellationToken?cancellationToken,
     CallCredentials credentials,
     CallTiming timing,
     Action <Metadata> headerMutation,
     WriteOptions writeOptions,
     ContextPropagationToken propagationToken) : this(cancellationToken, credentials, timing, headerMutation, writeOptions, propagationToken, null, null)
 {
 }
 /// <summary>
 /// Returns a new <see cref="CallSettings"/> with the specified call timing,
 /// merged with the (optional) original settings specified by <paramref name="settings"/>.
 /// </summary>
 /// <param name="settings">Original settings. May be null, in which case the returned settings
 /// will only contain call timing.</param>
 /// <param name="timing">Call timing for the new call settings.
 /// This may be null, in which case any call timing in <paramref name="settings"/> are
 /// not present in the new call settings. If both this and <paramref name="settings"/> are null,
 /// the return value is null.</param>
 /// <returns>A new set of call settings, or null if both parameters are null.</returns>
 public static CallSettings WithCallTiming(
     this CallSettings settings,
     CallTiming timing) =>
 settings == null
         ? CallSettings.FromCallTiming(timing)
         : new CallSettings(settings.CancellationToken, settings.Credentials,
                            timing, settings.HeaderMutation,
                            settings.WriteOptions, settings.PropagationToken,
                            settings.ResponseMetadataHandler, settings.TrailingMetadataHandler);
Beispiel #6
0
 /// <summary>
 /// Calculate a deadline from a <see cref="CallTiming"/> and a <see cref="IClock"/>.
 /// </summary>
 /// <param name="callTiming"><see cref="CallTiming"/>, may be null.</param>
 /// <param name="clock"><see cref="IClock"/> to use for deadline calculation.</param>
 /// <returns>The calculated absolute deadline, or null is no deadline should be used.</returns>
 /// <exception cref="InvalidOperationException">
 /// The <paramref name="callTiming"/> uses retry. Only a simple expiration is valid
 /// for calculating a deadline.
 /// </exception>
 internal static DateTime?CalculateDeadline(this CallTiming callTiming, IClock clock)
 {
     if (callTiming == null)
     {
         return(null);
     }
     if (callTiming.Type != CallTimingType.Expiration)
     {
         throw new InvalidOperationException("Cannot calculate deadline from retry.");
     }
     return(callTiming.Expiration.CalculateDeadline(clock));
 }
Beispiel #7
0
 /// <summary>
 /// Constructs an instance with the specified settings.
 /// </summary>
 /// <param name="cancellationToken">Cancellation token that can be used for cancelling the call.</param>
 /// <param name="credentials">Credentials to use for the call.</param>
 /// <param name="timing"><see cref="CallTiming"/> to use, or null for default retry/expiration behavior.</param>
 /// <param name="headerMutation">Action to modify the headers to send at the beginning of the call.</param>
 /// <param name="writeOptions"><see cref="global::Grpc.Core.WriteOptions"/> that will be used for the call.</param>
 /// <param name="propagationToken"><see cref="ContextPropagationToken"/> for propagating settings from a parent call.</param>
 public CallSettings(
     CancellationToken?cancellationToken,
     CallCredentials credentials,
     CallTiming timing,
     Action <Metadata> headerMutation,
     WriteOptions writeOptions,
     ContextPropagationToken propagationToken)
 {
     CancellationToken = cancellationToken;
     Credentials       = credentials;
     Timing            = timing;
     HeaderMutation    = headerMutation;
     WriteOptions      = writeOptions;
     PropagationToken  = propagationToken;
 }
Beispiel #8
0
 /// <summary>
 /// Constructs an instance with the specified settings.
 /// </summary>
 /// <param name="cancellationToken">Cancellation token that can be used for cancelling the call.</param>
 /// <param name="credentials">Credentials to use for the call.</param>
 /// <param name="timing"><see cref="CallTiming"/> to use, or null for default retry/expiration behavior.</param>
 /// <param name="headerMutation">Action to modify the headers to send at the beginning of the call.</param>
 /// <param name="writeOptions"><see cref="global::Grpc.Core.WriteOptions"/> that will be used for the call.</param>
 /// <param name="propagationToken"><see cref="ContextPropagationToken"/> for propagating settings from a parent call.</param>
 public CallSettings(
     CancellationToken? cancellationToken,
     CallCredentials credentials,
     CallTiming timing,
     Action<Metadata> headerMutation,
     WriteOptions writeOptions,
     ContextPropagationToken propagationToken)
 {
     CancellationToken = cancellationToken;
     Credentials = credentials;
     Timing = timing;
     HeaderMutation = headerMutation;
     WriteOptions = writeOptions;
     PropagationToken = propagationToken;
 }
Beispiel #9
0
        // By design, the code is mostly duplicated between the async and sync calls.

        // Async retry
        internal static Func <TRequest, CallSettings, Task <TResponse> > WithRetry <TRequest, TResponse>(
            this Func <TRequest, CallSettings, Task <TResponse> > fn,
            IClock clock, IScheduler scheduler, Func <TResponse, Task> postResponse) =>
        async(request, callSettings) =>
        {
            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
                {
                    var response = await fn(request, attemptCallSettings).ConfigureAwait(false);

                    if (postResponse != null)
                    {
                        await postResponse(response).ConfigureAwait(false);
                    }
                    return(response);
                }
                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);
                }
            }
        };
Beispiel #10
0
 /// <summary>
 /// Constructs an instance with the specified settings.
 /// </summary>
 /// <param name="cancellationToken">Cancellation token that can be used for cancelling the call.</param>
 /// <param name="credentials">Credentials to use for the call.</param>
 /// <param name="timing"><see cref="CallTiming"/> to use, or null for default retry/expiration behavior.</param>
 /// <param name="headerMutation">Action to modify the headers to send at the beginning of the call.</param>
 /// <param name="writeOptions"><see cref="global::Grpc.Core.WriteOptions"/> that will be used for the call.</param>
 /// <param name="propagationToken"><see cref="ContextPropagationToken"/> for propagating settings from a parent call.</param>
 /// <param name="responseMetadataHandler">Action to invoke when response metadata is received.</param>
 /// <param name="trailingMetadataHandler">Action to invoke when trailing metadata is received.</param>
 public CallSettings(
     CancellationToken?cancellationToken,
     CallCredentials credentials,
     CallTiming timing,
     Action <Metadata> headerMutation,
     WriteOptions writeOptions,
     ContextPropagationToken propagationToken,
     Action <Metadata> responseMetadataHandler,
     Action <Metadata> trailingMetadataHandler)
 {
     CancellationToken       = cancellationToken;
     Credentials             = credentials;
     Timing                  = timing;
     HeaderMutation          = headerMutation;
     WriteOptions            = writeOptions;
     PropagationToken        = propagationToken;
     ResponseMetadataHandler = responseMetadataHandler;
     TrailingMetadataHandler = trailingMetadataHandler;
 }
        /// <summary>
        /// Returns a <see cref="CallSettings"/> which will have an effective deadline of at least <paramref name="deadline"/>.
        /// If <paramref name="settings"/> already observes an earlier deadline (with respect to <paramref name="clock"/>),
        /// or if <paramref name="deadline"/> is null, the original settings will be returned.
        /// </summary>
        /// <param name="settings">Existing settings. May be null, meaning there are currently no settings.</param>
        /// <param name="deadline">Deadline to enforce. May be null, meaning there is no deadline to enforce.</param>
        /// <param name="clock">The clock to use when computing deadlines. Must not be null.</param>
        /// <returns>The call settings to use to observe the given deadline.</returns>
        public static CallSettings WithEarlierDeadline(this CallSettings settings, DateTime?deadline, IClock clock)
        {
            GaxPreconditions.CheckNotNull(clock, nameof(clock));
            // No deadline? Return what we already have.
            if (deadline == null)
            {
                return(settings);
            }
            // No settings, or no timing in the settings? Create a simple expiration.
            if (settings == null || settings.Timing == null)
            {
                // WithCallTiming (above) is an extension method - it's fine for settings to be null.
                return(settings.WithCallTiming(CallTiming.FromDeadline(deadline.Value)));
            }
            // Okay, we have settings with a genuine timing component and a new deadline.
            // We may still return the existing settings, if the new deadline is later than the existing
            // one in the settings. But if the new deadline is earlier, we need to build new settings to accommodate
            // it.
            var timing = settings.Timing;

            switch (timing.Type)
            {
            // For simple expirations, we can just replace one deadline for another simple one,
            // if necessary.
            case CallTimingType.Expiration:
                return(timing.Expiration.CalculateDeadline(clock) < deadline.Value
                        ? settings
                        : settings.WithCallTiming(CallTiming.FromDeadline(deadline.Value)));

            // For retries, we may need to create a new RetrySettings with all the same other aspects,
            // but a new deadline.
            case CallTimingType.Retry:
                if (timing.Retry.TotalExpiration.CalculateDeadline(clock) < deadline.Value)
                {
                    return(settings);
                }
                var newTiming = CallTiming.FromRetry(timing.Retry.WithTotalExpiration(Expiration.FromDeadline(deadline.Value)));
                return(settings.WithCallTiming(newTiming));

            default:
                throw new InvalidOperationException("Invalid call timing type");
            }
        }
Beispiel #12
0
 /// <summary>
 /// Creates a <see cref="CallSettings"/> for the specified call timing, or returns null
 /// if <paramref name="timing"/> is null.
 /// </summary>
 /// <param name="timing">The call timing for the new settings.</param>
 /// <returns>A new instance or null if <paramref name="timing"/> is null..</returns>
 public static CallSettings FromCallTiming(CallTiming timing) =>
 timing == null ? null : new CallSettings(null, null, timing, null, null, null);
Beispiel #13
0
 /// <summary>
 /// Creates a <see cref="CallSettings"/> for the specified call timing, or returns null
 /// if <paramref name="timing"/> is null.
 /// </summary>
 /// <param name="timing">The call timing for the new settings.</param>
 /// <returns>A new instance or null if <paramref name="timing"/> is null..</returns>
 public static CallSettings FromCallTiming(CallTiming timing) =>
     timing == null ? null : new CallSettings(null, null, timing, null, null, null);