public async Task CommitFailsOtherThanAborted() { var spannerClientMock = SpannerClientHelpers .CreateMockClient(Logger.DefaultLogger, MockBehavior.Strict) .SetupBatchCreateSessionsAsync() .SetupBeginTransactionAsync() .SetupExecuteBatchDmlAsync() .SetupCommitAsync_Fails(failures: 1, StatusCode.Unknown) .SetupRollbackAsync(); SpannerConnection connection = BuildSpannerConnection(spannerClientMock); var scheduler = (FakeScheduler)connection.Builder.SessionPoolManager.SpannerSettings.Scheduler; var time0 = scheduler.Clock.GetCurrentDateTimeUtc(); var callee = new Callee(scheduler); await scheduler.RunAsync(async() => { var exception = await Assert.ThrowsAsync <SpannerException>( () => connection.RunWithRetriableTransactionAsync( transaction => callee.DatabaseWorkAsync(connection, transaction))); Assert.Contains("Bang!", exception.InnerException.Message); }); callee.AssertBackoffTimesInRange(time0); callee.AssertLastCallTime(scheduler.Clock.GetCurrentDateTimeUtc()); }
public async Task BatchDmlAbortsTwice() { var spannerClientMock = SpannerClientHelpers .CreateMockClient(Logger.DefaultLogger, MockBehavior.Strict) .SetupBatchCreateSessionsAsync() .SetupBeginTransactionAsync() .SetupExecuteBatchDmlAsync_Fails(failures: 2, statusCode: StatusCode.Aborted) .SetupCommitAsync() .SetupRollbackAsync(); SpannerConnection connection = BuildSpannerConnection(spannerClientMock); var scheduler = (FakeScheduler)connection.Builder.SessionPoolManager.SpannerSettings.Scheduler; var time0 = scheduler.Clock.GetCurrentDateTimeUtc(); var callee = new Callee(scheduler); await scheduler.RunAsync(async() => { var result = await connection.RunWithRetriableTransactionAsync(transaction => callee.DatabaseWorkAsync(connection, transaction)); Assert.Equal(3, result); }); callee.AssertBackoffTimesInRange(time0); callee.AssertLastCallTime(scheduler.Clock.GetCurrentDateTimeUtc()); }
public async Task CommitAbortsAlways_RespectsOverallDeadline() { var spannerClientMock = SpannerClientHelpers .CreateMockClient(Logger.DefaultLogger, MockBehavior.Strict) .SetupBatchCreateSessionsAsync() .SetupBeginTransactionAsync() .SetupExecuteBatchDmlAsync() .SetupCommitAsync_FailsAlways(statusCode: StatusCode.Aborted) .SetupRollbackAsync(); SpannerConnection connection = BuildSpannerConnection(spannerClientMock); var scheduler = (FakeScheduler)connection.Builder.SessionPoolManager.SpannerSettings.Scheduler; // This test needs a little bit more of real time, else it's flaky. scheduler.RealTimeTimeout = TimeSpan.FromSeconds(60); var time0 = scheduler.Clock.GetCurrentDateTimeUtc(); var callee = new Callee(scheduler); await scheduler.RunAsync(async() => { var exception = await Assert.ThrowsAsync <SpannerException>(() => connection.RunWithRetriableTransactionAsync(transaction => callee.DatabaseWorkAsync(connection, transaction))); Assert.True(exception.IsRetryable && !exception.SessionExpired); Assert.Contains("Bang!", exception.InnerException.Message); }); // The minimum calls that can be made (assuming maximum jitter always) in that time is 60 * 60 / 32 * 2 which is 56.25, // plus 1 because the first one has no delay. // The maximum number of calls that can be made in 1 hour: // - Given that 2^7 * 250 = 32000, the first 8 calls take, at a minimum 31_750ms, approx 32s. // - The rest will happen at most (60 * 60 - 32) / 32 which is 111.5 callee.AssertCalledInRange(57, 120); // The overall deadline is of 1 hour. The maximum backoff delay is of 32s. // Because of jitter retries can stop at any time after 60mins - 64s, let's give it 2 minutes of range. Assert.InRange(scheduler.Clock.GetCurrentDateTimeUtc(), time0.AddMinutes(58), time0.AddMinutes(60)); }