예제 #1
0
        public void WithCallTiming_NullSettings()
        {
            CallSettings noSettings = null;

            Assert.Null(noSettings.WithCallTiming(null));
            CallTiming timing = CallTiming.FromExpiration(Expiration.None);
            var        result = noSettings.WithCallTiming(timing);

            Assert.Same(timing, result.Timing);
        }
예제 #2
0
        /// <summary>
        /// Updates the call settings with configuration parameters.
        /// </summary>
        /// <param name="callSettings">The call settings.</param>
        /// <param name="config">The configuration.</param>
        /// <param name="serviceContext">The service context.</param>
        /// <returns></returns>
        private CallSettings UpdateCallSettingsWithConfigParameters(CallSettings callSettings,
                                                                    GoogleAdsConfig config, GoogleAdsServiceContext serviceContext)
        {
            callSettings = callSettings.WithHeader(GoogleAdsConfig.DEVELOPER_TOKEN_KEYNAME,
                                                   config.DeveloperToken)
                           .WithResponseMetadataHandler(delegate(Metadata metadata)
            {
                GoogleAdsResponseMetadata responseMetadata =
                    new GoogleAdsResponseMetadata(metadata);
                serviceContext.OnResponseMetadataReceived(responseMetadata);
            });

            if (!string.IsNullOrEmpty(config.LoginCustomerId))
            {
                callSettings = callSettings.WithHeader(GoogleAdsConfig.LOGIN_CUSTOMER_ID_KEYNAME,
                                                       config.LoginCustomerId);
            }

            if (!string.IsNullOrEmpty(config.LibraryIdentifierOverride))
            {
                callSettings = callSettings.WithHeader(GoogleAdsConfig.LIBRARY_IDENTIFIER_KEYNAME,
                                                       config.LibraryIdentifierOverride);
            }

            callSettings = callSettings.WithCallTiming(CallTiming.FromTimeout(
                                                           TimeSpan.FromMilliseconds(config.Timeout)));

            return(callSettings);
        }
예제 #3
0
        /// <summary>
        /// Asynchronously polls the operation until it is complete, returning the completed operation.
        /// </summary>
        /// <remarks>
        /// <para>
        /// If this object already represents a completed operation, it is returned with no further RPCs.
        /// If <paramref name="metadataCallback"/> is non-null, the callback will be called with any metadata
        /// present before the result is returned.
        /// </para>
        /// <para>
        /// No guarantee is made as to which thread is used for metadata callbacks. However, each callback is
        /// performed synchronously: this method waits for the callback to complete before the operation is next polled.
        /// This guarantees that for a single call, metadata callbacks will never occur in parallel.
        /// </para>
        /// </remarks>
        /// <param name="pollSettings">The settings to use for repeated polling, or null
        /// to use the default poll settings (poll once every 10 seconds, forever).</param>
        /// <param name="callSettings">The call settings to apply on each call, or null for default settings.</param>
        /// <param name="metadataCallback">The callback to invoke with the metadata from each poll operation, even if the metadata is null.</param>
        /// <returns>The completed operation, which can then be checked for errors or a result.</returns>
        public Task <Operation <TResponse, TMetadata> > PollUntilCompletedAsync(
            PollSettings pollSettings           = null,
            CallSettings callSettings           = null,
            Action <TMetadata> metadataCallback = null)
        {
            if (IsCompleted)
            {
                metadataCallback?.Invoke(Metadata);
                return(Task.FromResult(this));
            }

            // We need to work out the effective timing so that we can truncate any deadline, but anything else
            // that's in the effective call settings can be left to the normal merging process. In particular,
            // we don't want to include the header mutation that adds the version header, as otherwise we'll end up
            // including it twice.
            var effectiveTiming = Client.GetEffectiveCallSettingsForGetOperation(callSettings)?.Timing;

            callSettings = callSettings.WithCallTiming(effectiveTiming);
            Func <DateTime?, Task <Operation <TResponse, TMetadata> > > pollAction =
                async deadline =>
            {
                var result = await PollOnceAsync(callSettings.WithEarlierDeadline(deadline, Client.Clock)).ConfigureAwait(false);

                metadataCallback?.Invoke(result.Metadata);
                return(result);
            };

            return(Polling.PollRepeatedlyAsync(
                       pollAction, o => o.IsCompleted,
                       Client.Clock, Client.Scheduler, pollSettings ?? Client.DefaultPollSettings ?? s_defaultPollSettings,
                       callSettings?.CancellationToken ?? CancellationToken.None));
        }
예제 #4
0
        public void WithCallTiming_NullTiming()
        {
            CallTiming        timing = CallTiming.FromExpiration(Expiration.None);
            CancellationToken token  = new CancellationTokenSource().Token;

            var original = new CallSettings(token, null, timing, null, null, null);
            var result   = original.WithCallTiming(null);

            Assert.Null(result.Timing);
            Assert.Equal(token, result.CancellationToken);
        }
        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);
        }
예제 #6
0
        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);
        }
예제 #7
0
        // 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);
            }
        }
예제 #8
0
        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);
                }
            }
        }
        public void WithCallTiming_NullTiming()
        {
            CallTiming timing = CallTiming.FromExpiration(Expiration.None);
            CancellationToken token = new CancellationTokenSource().Token;

            var original = new CallSettings(token, null, timing, null, null, null);
            var result = original.WithCallTiming(null);
            Assert.Null(result.Timing);
            Assert.Equal(token, result.CancellationToken);
        }