Example #1
0
        public void RetrySettingsTiming()
        {
            // Sample: RetrySettingsTiming
            // The overall operation mustn't take more than 30 seconds.
            Expiration totalExpiration = Expiration.FromTimeout(TimeSpan.FromSeconds(30));

            // The delay between one RPC finishing and when we make the next one.
            // Each delay is double the previous one, with a maximum of 5s.
            // The first delay is 1s, then 2s, then 4s, then 5s, then 5s, etc.
            BackoffSettings retryBackoff = new BackoffSettings(
                delay: TimeSpan.FromSeconds(1),
                maxDelay: TimeSpan.FromSeconds(5),
                delayMultiplier: 2.0);

            // How long each individual RPC is allowed to take.
            // Each timeout is 1.5x the previous one, with a maximum of 10s.
            // The first timeout is 4s, then 6s, then 9s, then 10s, then 10s etc.
            BackoffSettings timeoutBackoff = new BackoffSettings(
                delay: TimeSpan.FromSeconds(4),
                maxDelay: TimeSpan.FromSeconds(10),
                delayMultiplier: 1.5);

            RetrySettings settings = new RetrySettings(retryBackoff, timeoutBackoff, totalExpiration);
            // End sample
        }
        public async Task RecordErrorAndWait_RetryInfo()
        {
            var settings = new BackoffSettings(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5), 2.0);
            var mock     = new Mock <IScheduler>(MockBehavior.Strict);

            // Delay taken from retry info
            mock.Setup(s => s.Delay(TimeSpan.FromSeconds(3), default)).Returns(Task.FromResult(0));
            // Delay taken from backoff settings (which have still doubled, even when the first value wasn't used)
            mock.Setup(s => s.Delay(TimeSpan.FromSeconds(2), default)).Returns(Task.FromResult(0));

            // The first exception contains retry info, so we don't use the backoff settings
            var retryInfo = new Rpc.RetryInfo {
                RetryDelay = Duration.FromTimeSpan(TimeSpan.FromSeconds(3))
            };
            Metadata trailers = new Metadata
            {
                { RetryState.RetryInfoKey, retryInfo.ToByteArray() }
            };
            var exception1 = new RpcException(new Status(StatusCode.Unavailable, "Bang"), trailers);
            var exception2 = new RpcException(new Status(StatusCode.Unavailable, "Bang"));

            RetryState state = new RetryState(mock.Object, settings, RetrySettings.NoJitter, maxConsecutiveErrors: 5);

            Assert.True(state.CanRetry(exception1));
            await state.RecordErrorAndWaitAsync(exception1, default);

            Assert.True(state.CanRetry(exception2));
            await state.RecordErrorAndWaitAsync(exception2, default);
        }
        public async Task RecordErrorAndWait_BackoffSettingsObeyed()
        {
            var settings = new BackoffSettings(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5), 2.0);
            var mock     = new Mock <IScheduler>(MockBehavior.Strict);

            mock.Setup(s => s.Delay(TimeSpan.FromSeconds(1), default)).Returns(Task.FromResult(0));
            mock.Setup(s => s.Delay(TimeSpan.FromSeconds(2), default)).Returns(Task.FromResult(0));
            mock.Setup(s => s.Delay(TimeSpan.FromSeconds(4), default)).Returns(Task.FromResult(0));
            // Retry maxes out at 5 seconds
            mock.Setup(s => s.Delay(TimeSpan.FromSeconds(5), default)).Returns(Task.FromResult(0));
            // After reset
            mock.Setup(s => s.Delay(TimeSpan.FromSeconds(1), default)).Returns(Task.FromResult(0));

            var exception = new RpcException(new Status(StatusCode.Unavailable, "Bang"));

            var state = new RetryState(mock.Object, settings, RetrySettings.NoJitter, maxConsecutiveErrors: 5);

            await state.RecordErrorAndWaitAsync(exception, default);

            await state.RecordErrorAndWaitAsync(exception, default);

            await state.RecordErrorAndWaitAsync(exception, default);

            await state.RecordErrorAndWaitAsync(exception, default);

            state.Reset();
            await state.RecordErrorAndWaitAsync(exception, default);
        }
 internal RetryState(IScheduler scheduler, BackoffSettings backoffSettings, IJitter backoffJitter, int maxConsecutiveErrors)
 {
     _scheduler            = scheduler;
     _backoffSettings      = backoffSettings;
     _backoffJitter        = backoffJitter;
     _maxConsecutiveErrors = maxConsecutiveErrors;
     Reset();
 }
Example #5
0
 // TODO: Expose BackoffSettings.NextDelay so we don't need this copy if ApiCallRetryExtensions.WithRetry is not exposed.
 internal static TimeSpan NextDelay(this BackoffSettings settings, TimeSpan currentDelay)
 {
     checked
     {
         TimeSpan next = new TimeSpan((long)(currentDelay.Ticks * settings.DelayMultiplier));
         return(next < settings.MaxDelay ? next : settings.MaxDelay);
     }
 }
Example #6
0
        public void NextDelay(double current, double expectedNext)
        {
            var settings = new BackoffSettings(
                delay: TimeSpan.FromSeconds(1.0),
                maxDelay: TimeSpan.FromSeconds(5.0),
                delayMultiplier: 2.0);

            var expected = TimeSpan.FromSeconds(expectedNext);
            var actual   = settings.NextDelay(TimeSpan.FromSeconds(current));

            Assert.Equal(expected, actual);
        }
Example #7
0
        public void WithEarlierDeadline_DeadlineIsLaterThanExistingRetryTotalExpiration()
        {
            var backoffSettings = new BackoffSettings(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), 2.0);
            // Use a cancellation token to emphasize that it's not just the timing.
            var          token    = new CancellationTokenSource().Token;
            var          timing   = CallTiming.FromRetry(new RetrySettings(backoffSettings, backoffSettings, Expiration.FromDeadline(new DateTime(100L, DateTimeKind.Utc))));
            CallSettings settings = CallSettings.FromCancellationToken(token)
                                    .WithCallTiming(timing);
            DateTime?deadline = new DateTime(200L, DateTimeKind.Utc);

            Assert.Same(settings, settings.WithEarlierDeadline(deadline, new FakeClock()));
        }
        internal WatchStream(IScheduler scheduler, IWatchState state, Target target, FirestoreDb db, CancellationToken cancellationToken)
        {
            _scheduler = scheduler;
            _state     = state;
            _target    = target;
            _db        = db;
            _callbackCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
            _networkCancellationTokenSource  = CancellationTokenSource.CreateLinkedTokenSource(_callbackCancellationTokenSource.Token);

            // TODO: Make these configurable?
            _backoffSettings = new BackoffSettings(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(30), 2.0);
            _backoffJitter   = RetrySettings.RandomJitter;
        }
Example #9
0
        private async Task IncrementByOneAsync(SpannerConnection connection, bool orphanTransaction = false)
        {
            var              backoffSettings = new BackoffSettings(TimeSpan.FromMilliseconds(250), TimeSpan.FromSeconds(5), 1.5);
            TimeSpan         nextDelay       = TimeSpan.Zero;
            SpannerException spannerException;
            DateTime         deadline = DateTime.UtcNow.AddSeconds(30);

            while (true)
            {
                spannerException = null;
                try
                {
                    // We use manually created transactions here so the tests run on .NET Core.
                    using (var transaction = await connection.BeginTransactionAsync())
                    {
                        long current;
                        using (var cmd = connection.CreateSelectCommand($"SELECT Int64Value FROM {_fixture.TableName} WHERE K=@k"))
                        {
                            cmd.Parameters.Add("k", SpannerDbType.String, _key);
                            cmd.Transaction = transaction;
                            var fetched = await cmd.ExecuteScalarAsync().ConfigureAwait(false);

                            current = fetched is DBNull ? 0L : (long)fetched;
                        }
                        using (var cmd = connection.CreateUpdateCommand(_fixture.TableName))
                        {
                            cmd.Parameters.Add("k", SpannerDbType.String, _key);
                            cmd.Parameters.Add("Int64Value", SpannerDbType.Int64, current + 1);
                            cmd.Transaction = transaction;
                            await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);

                            if (!orphanTransaction)
                            {
                                await transaction.CommitAsync().ConfigureAwait(false);
                            }
                        }
                    }
                    return;
                }
                // Keep trying for up to 30 seconds
                catch (SpannerException ex) when(ex.IsRetryable && DateTime.UtcNow < deadline)
                {
                    nextDelay = backoffSettings.NextDelay(nextDelay);
                    await Task.Delay(RetrySettings.RandomJitter.GetDelay(nextDelay));

                    spannerException = ex;
                }
            }
        }
Example #10
0
        public void WithExpiration_SettingsWithRetry()
        {
            var          token           = new CancellationTokenSource().Token;
            var          backoffSettings = new BackoffSettings(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(10), 1.5);
            var          retry           = new RetrySettings(backoffSettings, backoffSettings, Expiration.FromTimeout(TimeSpan.FromSeconds(5)));
            var          originalTiming  = CallTiming.FromRetry(retry);
            CallSettings settings        = CallSettings.FromCancellationToken(token).WithCallTiming(originalTiming);
            Expiration   expiration      = Expiration.FromTimeout(TimeSpan.FromSeconds(1));
            var          result          = settings.WithExpiration(expiration);

            Assert.Same(expiration, result.Timing.Retry.TotalExpiration);
            Assert.Same(backoffSettings, result.Timing.Retry.RetryBackoff);
            Assert.Same(backoffSettings, result.Timing.Retry.TimeoutBackoff);
            Assert.Equal(token, result.CancellationToken);
        }
Example #11
0
        public void WithNewExpiration()
        {
            var retryBackoff   = new BackoffSettings(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3));
            var timeoutBackoff = new BackoffSettings(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3));
            var original       = new RetrySettings(retryBackoff, timeoutBackoff, Expiration.FromTimeout(TimeSpan.FromSeconds(5)),
                                                   e => false, RetrySettings.NoJitter);
            var newExpiration = Expiration.FromDeadline(DateTime.UtcNow);
            var newSettings   = original.WithTotalExpiration(newExpiration);

            Assert.Same(original.RetryBackoff, newSettings.RetryBackoff);
            Assert.Same(original.TimeoutBackoff, newSettings.TimeoutBackoff);
            Assert.Same(original.RetryFilter, newSettings.RetryFilter);
            Assert.Same(original.DelayJitter, newSettings.DelayJitter);
            Assert.Same(newExpiration, newSettings.TotalExpiration);
        }
 /// <summary>
 /// Constructor with complete control, for testing purposes.
 /// </summary>
 internal SqlResultStream(
     SpannerClient client,
     ExecuteSqlRequest request,
     Session session,
     CallSettings callSettings,
     int maxBufferSize,
     BackoffSettings backoffSettings,
     IJitter backoffJitter)
 {
     _buffer          = new LinkedList <PartialResultSet>();
     _client          = GaxPreconditions.CheckNotNull(client, nameof(client));
     _request         = GaxPreconditions.CheckNotNull(request, nameof(request));
     _session         = GaxPreconditions.CheckNotNull(session, nameof(session));
     _callSettings    = callSettings;
     _maxBufferSize   = GaxPreconditions.CheckArgumentRange(maxBufferSize, nameof(maxBufferSize), 1, 10_000);
     _backoffSettings = GaxPreconditions.CheckNotNull(backoffSettings, nameof(backoffSettings));
     _backoffJitter   = GaxPreconditions.CheckNotNull(backoffJitter, nameof(backoffJitter));
 }
Example #13
0
 public LogUploader(
     LoggingServiceV2Client client,
     IScheduler scheduler,
     IClock clock,
     ILogQueue logQ,
     LogEntry logsLostWarningTemplate,
     int maxUploadBatchSize,
     BackoffSettings serverErrorBackoffSettings)
 {
     _client    = client;
     _scheduler = scheduler;
     _clock     = clock;
     _logQ      = logQ;
     _logsLostWarningTemplate    = logsLostWarningTemplate;
     _maxUploadBatchSize         = maxUploadBatchSize;
     _serverErrorBackoffSettings = serverErrorBackoffSettings;
     _uploaderTask = Task.Run(RunUploader);
 }
Example #14
0
        // [START retry]
        /// <summary>
        /// Creates new CallSettings that will retry an RPC that fails.
        /// </summary>
        /// <param name="tryCount">
        /// How many times to try the RPC before giving up?
        /// </param>
        /// <param name="finalStatusCodes">
        /// Which status codes should we *not* retry?
        /// </param>
        /// <returns>
        /// A CallSettings instance.
        /// </returns>
        CallSettings newRetryCallSettings(int tryCount,
                                          params StatusCode[] finalStatusCodes)
        {
            // Initialize values for backoff settings to be used
            // by the CallSettings for RPC retries
            var backoff = new BackoffSettings(
                delay: TimeSpan.FromMilliseconds(500),
                maxDelay: TimeSpan.FromSeconds(3), delayMultiplier: 2);

            return(new CallSettings(null, null,
                                    CallTiming.FromRetry(new RetrySettings(backoff, backoff,
                                                                           Google.Api.Gax.Expiration.None,
                                                                           (RpcException e) => (
                                                                               StatusCode.OK != e.Status.StatusCode &&
                                                                               !finalStatusCodes.Contains(e.Status.StatusCode) &&
                                                                               --tryCount > 0),
                                                                           RetrySettings.NoJitter)),
                                    metadata => metadata.Add("ClientVersion", "1.0.0"), null, null));
        }
Example #15
0
        // [START retry]
        /// <summary>
        /// Creates new CallSettings that will retry an RPC that fails.
        /// </summary>
        /// <param name="tryCount">
        /// How many times to try the RPC before giving up?
        /// </param>
        /// <param name="finalStatusCodes">
        /// Which status codes should we *not* retry?
        /// </param>
        /// <returns>
        /// A CallSettings instance.
        /// </returns>
        CallSettings newRetryCallSettings(int tryCount,
                                          params StatusCode[] finalStatusCodes)
        {
            var backoff = new BackoffSettings()
            {
                Delay           = TimeSpan.FromMilliseconds(500),
                DelayMultiplier = 2,
                MaxDelay        = TimeSpan.FromSeconds(3)
            };

            return(new CallSettings()
            {
                Timing = CallTiming.FromRetry(new RetrySettings()
                {
                    RetryBackoff = backoff,
                    TimeoutBackoff = backoff,
                    RetryFilter = (RpcException e) => (
                        StatusCode.OK != e.Status.StatusCode &&
                        !finalStatusCodes.Contains(e.Status.StatusCode) &&
                        --tryCount > 0),
                    DelayJitter = RetrySettings.NoJitter,
                })
            });
        }
Example #16
0
        /// <inheritdoc/>
        public override void ActivateOptions()
        {
            // Validate configuration
            GaxPreconditions.CheckState(string.IsNullOrWhiteSpace(CredentialFile) || string.IsNullOrWhiteSpace(CredentialJson),
                                        $"{nameof(CredentialFile)} and {nameof(CredentialJson)} must not both be set.");
            GaxPreconditions.CheckState(LogId != null, $"{nameof(LogId)} must be set.");
            GaxPreconditions.CheckState(MaxUploadBatchSize > 0, $"{nameof(MaxUploadBatchSize)} must be > 0");
            GaxPreconditions.CheckEnumValue <LocalQueueType>(LocalQueueType, nameof(LocalQueueType));

            base.ActivateOptions();

            // Initialise services if not already initialised for testing
            _client    = _client ?? BuildLoggingServiceClient();
            _scheduler = _scheduler ?? SystemScheduler.Instance;
            _clock     = _clock ?? SystemClock.Instance;
            _platform  = _platform ?? Platform.Instance();

            // Normalize string configuration
            ResourceType = string.IsNullOrWhiteSpace(ResourceType) ? null : ResourceType;
            ProjectId    = string.IsNullOrWhiteSpace(ProjectId) ? null : ProjectId;
            LogId        = string.IsNullOrWhiteSpace(LogId) ? null : LogId;

            switch (LocalQueueType)
            {
            case LocalQueueType.Memory:
                GaxPreconditions.CheckState(MaxMemoryCount > 0 || MaxMemorySize > 0,
                                            $"Either {nameof(MaxMemoryCount)} or {nameof(MaxMemorySize)} must be configured to be > 0");
                break;

            default:
                throw new InvalidOperationException($"Invalid {nameof(Log4Net.LocalQueueType)}: '{LocalQueueType}'");
            }
            GaxPreconditions.CheckState(ServerErrorBackoffDelaySeconds >= 1,
                                        $"{nameof(ServerErrorBackoffDelaySeconds)} must be >= 1 second.");
            GaxPreconditions.CheckState(ServerErrorBackoffMultiplier > 1.1999999,
                                        $"{nameof(ServerErrorBackoffMultiplier)} must be >= 1.2");
            GaxPreconditions.CheckState(ServerErrorBackoffMaxDelaySeconds >= 20,
                                        $"{nameof(ServerErrorBackoffMaxDelaySeconds)} must be >= 20 seconds.");

            ActivateLogIdAndResource();
            switch (LocalQueueType)
            {
            case LocalQueueType.Memory:
                _logQ = new MemoryLogQueue(MaxMemorySize, MaxMemoryCount);
                break;

            default:
                throw new InvalidOperationException($"Invalid {nameof(Log4Net.LocalQueueType)}: '{LocalQueueType}'");
            }
            _initIdTask = Task.Run(_logQ.GetPreviousExecutionIdAsync);
            var logsLostWarningEntry = new LogEntry
            {
                TextPayload = s_logsLostWarningMessage,
                Severity    = LogSeverity.Warning,
                LogName     = _logName,
                Resource    = _resource,
                // Patterns included in custom labels will not be used in this "logs lost" entry.
                // The pattern itself will be logged, regardless of the "UsePatternWithinCustomLabels" setting.
                // This is acceptable as most patterns will be irrelevant in this context.
                Labels = { _customLabels.ToDictionary(x => x.Key, x => x.Value) },
            };
            var serverErrorBackoffSettings = new BackoffSettings(
                delay: TimeSpan.FromSeconds(ServerErrorBackoffDelaySeconds),
                delayMultiplier: ServerErrorBackoffMultiplier,
                maxDelay: TimeSpan.FromSeconds(ServerErrorBackoffMaxDelaySeconds)
                );

            _logUploader = new LogUploader(
                _client, _scheduler, _clock,
                _logQ, logsLostWarningEntry, MaxUploadBatchSize,
                serverErrorBackoffSettings);
            if (_usePatternWithinCustomLabels)
            {
                // Initialize a pattern layout for each custom label.
                _customLabelsPatterns = _customLabels.Select(x => new CustomLabelPattern(x.Key, new PatternLayout(x.Value))).ToArray();
            }
            _isActivated = true;
        }
Example #17
0
        /// <inheritdoc/>
        public override void ActivateOptions()
        {
            base.ActivateOptions();

            // Initialise services if not already initialised for testing
            _client    = _client ?? LoggingServiceV2Client.Create();
            _scheduler = _scheduler ?? SystemScheduler.Instance;
            _clock     = _clock ?? SystemClock.Instance;
            _platform  = _platform ?? Platform.Instance();

            // Normalize string configuration
            ResourceType = string.IsNullOrWhiteSpace(ResourceType) ? null : ResourceType;
            ProjectId    = string.IsNullOrWhiteSpace(ProjectId) ? null : ProjectId;
            LogId        = string.IsNullOrWhiteSpace(LogId) ? null : LogId;

            // Validate configuration
            GaxPreconditions.CheckState(LogId != null, $"{nameof(LogId)} must be set.");
            GaxPreconditions.CheckState(MaxUploadBatchSize > 0, $"{nameof(MaxUploadBatchSize)} must be > 0");
            GaxPreconditions.CheckEnumValue <LocalQueueType>(LocalQueueType, nameof(LocalQueueType));
            switch (LocalQueueType)
            {
            case LocalQueueType.Memory:
                GaxPreconditions.CheckState(MaxMemoryCount > 0 || MaxMemorySize > 0,
                                            $"Either {nameof(MaxMemoryCount)} or {nameof(MaxMemorySize)} must be configured to be > 0");
                break;

            default:
                throw new InvalidOperationException($"Invalid {nameof(Log4Net.LocalQueueType)}: '{LocalQueueType}'");
            }
            GaxPreconditions.CheckState(ServerErrorBackoffDelaySeconds >= 1,
                                        $"{nameof(ServerErrorBackoffDelaySeconds)} must be >= 1 second.");
            GaxPreconditions.CheckState(ServerErrorBackoffMultiplier > 1.1999999,
                                        $"{nameof(ServerErrorBackoffMultiplier)} must be >= 1.2");
            GaxPreconditions.CheckState(ServerErrorBackoffMaxDelaySeconds >= 20,
                                        $"{nameof(ServerErrorBackoffMaxDelaySeconds)} must be >= 20 seconds.");

            ActivateLogIdAndResource();
            switch (LocalQueueType)
            {
            case LocalQueueType.Memory:
                _logQ = new MemoryLogQueue(MaxMemorySize, MaxMemoryCount);
                break;

            default:
                throw new InvalidOperationException($"Invalid {nameof(Log4Net.LocalQueueType)}: '{LocalQueueType}'");
            }
            _initIdTask = Task.Run(_logQ.GetPreviousExecutionIdAsync);
            var labels = new Dictionary <string, string>();

            foreach (var customLabel in _customLabels)
            {
                labels.Add(customLabel.Key, customLabel.Value);
            }
            var logsLostWarningEntry = new LogEntry
            {
                TextPayload = s_logsLostWarningMessage,
                Severity    = LogSeverity.Warning,
                LogName     = _logName,
                Resource    = _resource,
                Labels      = { _customLabels.ToDictionary(x => x.Key, x => x.Value) },
            };
            var serverErrorBackoffSettings = new BackoffSettings(
                delay: TimeSpan.FromSeconds(ServerErrorBackoffDelaySeconds),
                delayMultiplier: ServerErrorBackoffMultiplier,
                maxDelay: TimeSpan.FromSeconds(ServerErrorBackoffMaxDelaySeconds)
                );

            _logUploader = new LogUploader(
                _client, _scheduler, _clock,
                _logQ, logsLostWarningEntry, MaxUploadBatchSize,
                serverErrorBackoffSettings);
            _isActivated = true;
        }
 internal RetryState(IScheduler scheduler, BackoffSettings backoffSettings, IJitter backoffJitter)
     : this(scheduler, backoffSettings, backoffJitter, DefaultMaxConsecutiveErrors)
 {
 }
 // [START retry]
 /// <summary>
 /// Creates new CallSettings that will retry an RPC that fails.
 /// </summary>
 /// <param name="tryCount">
 /// How many times to try the RPC before giving up?
 /// </param>
 /// <param name="finalStatusCodes">
 /// Which status codes should we *not* retry?
 /// </param>
 /// <returns>
 /// A CallSettings instance.
 /// </returns>
 CallSettings newRetryCallSettings(int tryCount,
     params StatusCode[] finalStatusCodes)
 {
     var backoff = new BackoffSettings()
     {
         Delay = TimeSpan.FromMilliseconds(500),
         DelayMultiplier = 2,
         MaxDelay = TimeSpan.FromSeconds(3)
     };
     return new CallSettings()
     {
         Timing = CallTiming.FromRetry(new RetrySettings()
         {
             RetryBackoff = backoff,
             TimeoutBackoff = backoff,
             RetryFilter = (RpcException e) => (
                 StatusCode.OK != e.Status.StatusCode
                 && !finalStatusCodes.Contains(e.Status.StatusCode)
                 && --tryCount > 0),
             DelayJitter = RetrySettings.NoJitter,
         })
     };
 }
        public override void ActivateOptions()
        {
            base.ActivateOptions();

            // Initialise services if not already initialised for testing
            _client    = _client ?? LoggingServiceV2Client.Create();
            _scheduler = _scheduler ?? SystemScheduler.Instance;
            _clock     = _clock ?? SystemClock.Instance;

            // Validate configuration
            GaxPreconditions.CheckState(!string.IsNullOrEmpty(ResourceType), $"{nameof(ResourceType)} must be set.");
            GaxPreconditions.CheckState(!string.IsNullOrEmpty(ProjectId), $"{nameof(ProjectId)} must be set.");
            GaxPreconditions.CheckState(!string.IsNullOrEmpty(LogId), $"{nameof(LogId)} must be set.");
            GaxPreconditions.CheckState(MaxUploadBatchSize > 0, $"{nameof(MaxUploadBatchSize)} must be > 0");
            GaxPreconditions.CheckEnumValue <LocalQueueType>(LocalQueueType, nameof(LocalQueueType));
            switch (LocalQueueType)
            {
            case LocalQueueType.Memory:
                GaxPreconditions.CheckState(MaxMemoryCount > 0 || MaxMemorySize > 0,
                                            $"Either {nameof(MaxMemoryCount)} or {nameof(MaxFileSize)} must be configured to be > 0");
                break;

            case LocalQueueType.Disk:
                GaxPreconditions.CheckState(!string.IsNullOrEmpty(File), $"{nameof(File)} must be set.");
                GaxPreconditions.CheckState(MaxFileSize > 0, $"{nameof(MaxFileSize)} must be > 0");
                GaxPreconditions.CheckState(MaxSizeRollBackups > 0, $"{nameof(MaxSizeRollBackups)} must be > 0");
                break;

            default:
                throw new InvalidOperationException("Inconceivable!");
            }
            GaxPreconditions.CheckState(ServerErrorBackoffDelaySeconds >= 1,
                                        $"{nameof(ServerErrorBackoffDelaySeconds)} must be >= 1 second.");
            GaxPreconditions.CheckState(ServerErrorBackoffMultiplier > 1.1999999,
                                        $"{nameof(ServerErrorBackoffMultiplier)} must be >= 1.2");
            GaxPreconditions.CheckState(ServerErrorBackoffMaxDelaySeconds >= 20,
                                        $"{nameof(ServerErrorBackoffMaxDelaySeconds)} must be >= 20 seconds.");

            // Configure the logger from the given configuration
            _logName  = LoggingServiceV2Client.FormatLogName(ProjectId, LogId);
            _resource = new MonitoredResource
            {
                Type = ResourceType
            };
            switch (LocalQueueType)
            {
            case LocalQueueType.Memory:
                _logQ = new MemoryLogQueue(MaxMemorySize, MaxMemoryCount);
                break;

            case LocalQueueType.Disk:
                throw new NotImplementedException("File-base local queues not implemented.");

            default:
                throw new InvalidOperationException("Inconceivable!");
            }
            _initNextIdTask = Task.Run(InitNextId);
            var labels = new Dictionary <string, string>();

            foreach (var customLabel in _customLabels)
            {
                labels.Add(customLabel.Key, customLabel.Value);
            }
            var logsLostWarningEntry = new LogEntry
            {
                TextPayload = s_logsLostWarningMessage,
                Severity    = LogSeverity.Warning,
                LogName     = _logName,
                Resource    = _resource,
                Labels      = { labels },
            };
            var serverErrorBackoffSettings = new BackoffSettings
            {
                Delay           = TimeSpan.FromSeconds(ServerErrorBackoffDelaySeconds),
                DelayMultiplier = ServerErrorBackoffMultiplier,
                MaxDelay        = TimeSpan.FromSeconds(ServerErrorBackoffMaxDelaySeconds),
            };

            _logUploader = new LogUploader(
                _client, _scheduler, _clock,
                _logQ, logsLostWarningEntry, MaxUploadBatchSize,
                serverErrorBackoffSettings);
        }