public async Task ResetDeadline() { var clock = new FakeClock(0); var scheduler = new AdvanceFakeClockScheduler(clock); var callSettings = CallSettings.FromExpiration(Expiration.FromDeadline(new DateTime(TimeSpan.FromSeconds(7).Ticks, DateTimeKind.Utc))); var state = new RetryState(clock, scheduler, s_retrySettings, callSettings); var retryInfo = new Rpc.RetryInfo { RetryDelay = Duration.FromTimeSpan(TimeSpan.FromSeconds(3)) }; Metadata trailers = new Metadata { { RetryState.RetryInfoKey, retryInfo.ToByteArray() } }; var exception = new RpcException(new Status(StatusCode.Unavailable, "Bang"), trailers); Assert.True(state.CanRetry(exception)); await state.WaitAsync(exception, default); Assert.True(state.CanRetry(exception)); await state.WaitAsync(exception, default); // Reset does not change the absolute deadline of the call. // The next retry attempt will therefore fail. state.Reset(); Assert.True(state.CanRetry(exception)); await Assert.ThrowsAsync <RpcException>(() => state.WaitAsync(exception, default)); Assert.Equal(TimeSpan.FromSeconds(6).Ticks, clock.GetCurrentDateTimeUtc().Ticks); }
public async Task ConsecutiveErrors_FailsRetryWhenDeadlineExceeded() { var clock = new FakeClock(0); var scheduler = new AdvanceFakeClockScheduler(clock); var callSettings = CallSettings.FromExpiration(Expiration.FromDeadline(new DateTime(TimeSpan.FromSeconds(7).Ticks, DateTimeKind.Utc))); var state = new RetryState(clock, scheduler, s_retrySettings, callSettings); var retryInfo = new Rpc.RetryInfo { RetryDelay = Duration.FromTimeSpan(TimeSpan.FromSeconds(3)) }; Metadata trailers = new Metadata { { RetryState.RetryInfoKey, retryInfo.ToByteArray() } }; var exception = new RpcException(new Status(StatusCode.Unavailable, "Bang"), trailers); Assert.True(state.CanRetry(exception)); await state.WaitAsync(exception, default); Assert.True(state.CanRetry(exception)); await state.WaitAsync(exception, default); Assert.True(state.CanRetry(exception)); await Assert.ThrowsAsync <RpcException>(() => state.WaitAsync(exception, default)); // Verify that the clock has been advanced 6 seconds. Assert.Equal(TimeSpan.FromSeconds(6).Ticks, clock.GetCurrentDateTimeUtc().Ticks); }
public async Task ErrorWithBackoffAfterDeadline_FailsRetry() { // Create a clock that starts at zero ticks. var clock = new FakeClock(0); var scheduler = new AdvanceFakeClockScheduler(clock); var callSettings = CallSettings.FromExpiration(Expiration.FromDeadline(new DateTime(TimeSpan.FromSeconds(10).Ticks, DateTimeKind.Utc))); var state = new RetryState(clock, scheduler, s_retrySettings, callSettings); // The retry info contains a wait time that is past the deadline of the call. // The retry state will throw a DeadlineExceeded error without waiting. var retryInfo = new Rpc.RetryInfo { RetryDelay = Duration.FromTimeSpan(TimeSpan.FromSeconds(20)) }; Metadata trailers = new Metadata { { RetryState.RetryInfoKey, retryInfo.ToByteArray() } }; var exception = new RpcException(new Status(StatusCode.Unavailable, "Bang"), trailers); Assert.True(state.CanRetry(exception)); await Assert.ThrowsAsync <RpcException>(() => state.WaitAsync(exception, default)); // Check that the clock has not been advanced to verify that the retry state did not wait 20 seconds // before throwing an exception. Assert.Equal(0, clock.GetCurrentDateTimeUtc().Ticks); }
public void ToCallOptions_CallTimingExpirationTimeout() { var clock = new FakeClock(); var timeout = TimeSpan.FromSeconds(1); CallSettings callSettings = CallSettings.FromCallTiming(CallTiming.FromExpiration(Expiration.FromTimeout(timeout))); var options = callSettings.ToCallOptions(clock); // Value should be exact, as we control time precisely. Assert.Equal(options.Deadline.Value, clock.GetCurrentDateTimeUtc() + timeout); }
public void WithEarlierDeadline_DeadlineIsLaterThanExistingTimeout() { // Use a cancellation token to emphasize that it's not just the timing. var token = new CancellationTokenSource().Token; CallSettings settings = CallSettings.FromCancellationToken(token) .WithTimeout(TimeSpan.FromTicks(100)); var clock = new FakeClock(); DateTime?deadline = clock.GetCurrentDateTimeUtc() + TimeSpan.FromTicks(200); Assert.Same(settings, settings.WithEarlierDeadline(deadline, clock)); }
public void WithEarlierDeadline_DeadlineIsEarlierThanExistingTimeout() { // Use a cancellation token to emphasize that it's not just the timing. var token = new CancellationTokenSource().Token; CallSettings settings = CallSettings.FromCancellationToken(token) .WithTimeout(TimeSpan.FromTicks(200)); var clock = new FakeClock(); DateTime? deadline = clock.GetCurrentDateTimeUtc() + TimeSpan.FromTicks(100); CallSettings newSettings = settings.WithEarlierDeadline(deadline, new FakeClock()); // New timing has a deadline rather than a timeout Assert.Equal(deadline, newSettings.Expiration.Deadline); Assert.Equal(token, newSettings.CancellationToken); }
public async Task LogEntriesLostSingleAppend_CheckTimestamps() { var fakeClock = new FakeClock(new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var uploadedEntries = await RunTestWorkingServer( async appender => { var logs = Enumerable.Range(0, 5).Select(i => CreateLog(Level.Info, $"Message{i}", i)).ToArray(); appender.DoAppend(logs); Assert.True(await appender.FlushAsync(s_testTimeout)); }, clock : fakeClock, maxMemoryCount : 1); // Expect logs-loss warning, and the last log entry Assert.Equal(2, uploadedEntries.Count); // Check the "lost-entries" payload text and timestamp are correct Assert.Equal(string.Format(s_lostMsg, TimeOfs(0), TimeOfs(3)), uploadedEntries[0].TextPayload); Assert.Equal(fakeClock.GetCurrentDateTimeUtc(), uploadedEntries[0].Timestamp.ToDateTime()); // Check the single successfully logged message is correct Assert.Equal("Message4", uploadedEntries[1].TextPayload.Trim()); }
public async Task ResetTimeout() { var clock = new FakeClock(0); var scheduler = new AdvanceFakeClockScheduler(clock); var callSettings = CallSettings.FromExpiration(Expiration.FromTimeout(TimeSpan.FromSeconds(7))); var state = new RetryState(clock, scheduler, s_retrySettings, callSettings); var retryInfo = new Rpc.RetryInfo { RetryDelay = Duration.FromTimeSpan(TimeSpan.FromSeconds(3)) }; Metadata trailers = new Metadata { { RetryState.RetryInfoKey, retryInfo.ToByteArray() } }; var exception = new RpcException(new Status(StatusCode.Unavailable, "Bang"), trailers); Assert.True(state.CanRetry(exception)); await state.WaitAsync(exception, default); Assert.True(state.CanRetry(exception)); await state.WaitAsync(exception, default); // Reset should set the deadline of the call to CurrentTime + Timeout. // That means that we can do two new retries without a timeout exception. state.Reset(); Assert.True(state.CanRetry(exception)); await state.WaitAsync(exception, default); Assert.True(state.CanRetry(exception)); await state.WaitAsync(exception, default); Assert.True(state.CanRetry(exception)); await Assert.ThrowsAsync <RpcException>(() => state.WaitAsync(exception, default)); // Verify that the clock has been advanced 12 seconds. Assert.Equal(TimeSpan.FromSeconds(12).Ticks, clock.GetCurrentDateTimeUtc().Ticks); }