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(); }
// 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); } }
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); }
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; }
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; } } }
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); }
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)); }
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); }
// [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)); }
// [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, }) }); }
/// <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; }
/// <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); }