public async Task ReadWriteTransaction_QueryFullyConsumed_AndThenError_FailsRetry() { string sql = $"SELECT Id FROM Foo WHERE Id={_fixture.RandomLong()}"; _fixture.SpannerMock.AddOrUpdateStatementResult(sql, StatementResult.CreateSingleColumnResultSet(new V1.Type { Code = V1.TypeCode.Int64 }, "Id", 1)); using var connection = CreateConnection(); await connection.OpenAsync(); using var transaction = await connection.BeginTransactionAsync(); var cmd = connection.CreateSelectCommand(sql); cmd.Transaction = transaction; using (var reader = await cmd.ExecuteReaderAsync()) { while (await reader.ReadAsync()) { Assert.Equal(1, reader.GetInt64(reader.GetOrdinal("Id"))); } } // Replace the result returned by the query on the server with an error and abort the transaction. // The retry should now fail. _fixture.SpannerMock.AddOrUpdateStatementResult(sql, StatementResult.CreateException(new RpcException(new Status(StatusCode.NotFound, "Table not found: Foo")))); _fixture.SpannerMock.AbortTransaction(transaction.TransactionId); await Assert.ThrowsAsync <SpannerAbortedDueToConcurrentModificationException>(() => transaction.CommitAsync()); Assert.Equal(1, transaction.RetryCount); }
public async Task ReadWriteTransaction_BatchDmlWithSameExceptionHalfwayAndDifferentResults_FailsRetry() { string sql1 = $"UPDATE Foo SET Bar='valid' WHERE Id={_fixture.RandomLong()}"; string sql2 = $"UPDATE Foo SET Bar='invalid' Id={_fixture.RandomLong()}"; _fixture.SpannerMock.AddOrUpdateStatementResult(sql1, StatementResult.CreateUpdateCount(1)); _fixture.SpannerMock.AddOrUpdateStatementResult(sql2, StatementResult.CreateException(new RpcException(new Status(StatusCode.FailedPrecondition, "UPDATE statement misses WHERE clause")))); using var connection = CreateConnection(); await connection.OpenAsync(); using var transaction = await connection.BeginTransactionAsync(); var cmd = connection.CreateBatchDmlCommand(); cmd.Transaction = transaction; cmd.Add(sql1); cmd.Add(sql2); var e = await Assert.ThrowsAsync <SpannerBatchNonQueryException>(() => cmd.ExecuteNonQueryAsync()); Assert.Contains("UPDATE statement misses WHERE clause", e.Message); Assert.Equal(new List <long> { 1 }, e.SuccessfulCommandResults); // Change the result of the first statement and abort the transaction. // The retry should now fail, even though the error code and message is the same. _fixture.SpannerMock.AddOrUpdateStatementResult(sql1, StatementResult.CreateUpdateCount(2)); _fixture.SpannerMock.AbortTransaction(transaction.TransactionId); await Assert.ThrowsAsync <SpannerAbortedDueToConcurrentModificationException>(() => transaction.CommitAsync()); Assert.Equal(1, transaction.RetryCount); }
public async Task ReadWriteTransaction_QueryWithError_AndThenNoError_FailsRetry() { string sql = $"SELECT Id FROM Foo WHERE Id={_fixture.RandomLong()}"; _fixture.SpannerMock.AddOrUpdateStatementResult(sql, StatementResult.CreateException(new RpcException(new Status(StatusCode.NotFound, "Table not found: Foo")))); using var connection = CreateConnection(); await connection.OpenAsync(); using var transaction = await connection.BeginTransactionAsync(); var cmd = connection.CreateSelectCommand(sql); cmd.Transaction = transaction; using (var reader = await cmd.ExecuteReaderAsync()) { // Any query error is thrown by the first call to reader.ReadAsync(); var e = await Assert.ThrowsAsync <SpannerException>(() => reader.ReadAsync()); Assert.Contains("Table not found: Foo", e.InnerException.Message); } // Remove the error returned by the query on the server and abort the transaction. // The retry should now fail. _fixture.SpannerMock.AddOrUpdateStatementResult(sql, StatementResult.CreateSingleColumnResultSet(new V1.Type { Code = V1.TypeCode.Int64 }, "Id", 1)); _fixture.SpannerMock.AbortTransaction(transaction.TransactionId); await Assert.ThrowsAsync <SpannerAbortedDueToConcurrentModificationException>(() => transaction.CommitAsync()); Assert.Equal(1, transaction.RetryCount); }
public async Task ReadWriteTransaction_BatchDmlWithDifferentException_FailsRetry() { string sql1 = $"UPDATE Foo SET Bar='bar' WHERE Id={_fixture.RandomLong()}"; _fixture.SpannerMock.AddOrUpdateStatementResult(sql1, StatementResult.CreateException(new RpcException(new Status(StatusCode.AlreadyExists, "Unique key constraint violation")))); using var connection = CreateConnection(); await connection.OpenAsync(); using var transaction = await connection.BeginTransactionAsync(); var cmd = connection.CreateBatchDmlCommand(); cmd.Transaction = transaction; cmd.Add(sql1); try { await cmd.ExecuteNonQueryAsync(); Assert.True(false, "Missing expected exception"); } catch (SpannerException e) when(e.ErrorCode == ErrorCode.AlreadyExists) { Assert.Contains("Unique key constraint violation", e.InnerException?.Message); } // Remove the error message for the udpate statement. _fixture.SpannerMock.AddOrUpdateStatementResult(sql1, StatementResult.CreateUpdateCount(1)); // Abort the transaction and try to commit. That will trigger a retry, but the retry // will not receive an error for the update statement. That will fail the retry. _fixture.SpannerMock.AbortTransaction(transaction.TransactionId); await Assert.ThrowsAsync <SpannerAbortedDueToConcurrentModificationException>(() => transaction.CommitAsync()); Assert.Equal(1, transaction.RetryCount); }
public async Task ReadWriteTransaction_AbortedDmlWithDifferentException_FailsRetry() { string sql = $"UPDATE Foo SET Bar='bar' WHERE Id={_fixture.RandomLong()}"; _fixture.SpannerMock.AddOrUpdateStatementResult(sql, StatementResult.CreateException(new RpcException(new Status(StatusCode.AlreadyExists, "Unique key constraint violation")))); using var connection = CreateConnection(); await connection.OpenAsync(); using var transaction = await connection.BeginTransactionAsync(); var cmd = connection.CreateDmlCommand(sql); cmd.Transaction = transaction; var e = await Assert.ThrowsAsync <SpannerException>(() => cmd.ExecuteNonQueryAsync()); Assert.Equal(ErrorCode.AlreadyExists, e.ErrorCode); Assert.Contains("Unique key constraint violation", e.InnerException?.Message); // Change the error for the statement on the mock server and abort the transaction. // The retry should now fail as the error has changed. _fixture.SpannerMock.AddOrUpdateStatementResult(sql, StatementResult.CreateException(new RpcException(new Status(StatusCode.NotFound, "Table Foo not found")))); _fixture.SpannerMock.AbortTransaction(transaction.TransactionId); await Assert.ThrowsAsync <SpannerAbortedDueToConcurrentModificationException>(() => transaction.CommitAsync()); Assert.Equal(1, transaction.RetryCount); }
public async Task ReadWriteTransaction_AbortedDmlWithSameException_CanBeRetried(bool enableInternalRetries) { string sql = $"UPDATE Foo SET Bar='bar' Id={_fixture.RandomLong()}"; _fixture.SpannerMock.AddOrUpdateStatementResult(sql, StatementResult.CreateException(new RpcException(new Status(StatusCode.FailedPrecondition, "UPDATE statement misses WHERE clause")))); using var connection = CreateConnection(); await connection.OpenAsync(); using var transaction = await connection.BeginTransactionAsync(); transaction.EnableInternalRetries = enableInternalRetries; var cmd = connection.CreateDmlCommand(sql); cmd.Transaction = transaction; var e = await Assert.ThrowsAsync <SpannerException>(() => cmd.ExecuteNonQueryAsync()); Assert.Equal(ErrorCode.FailedPrecondition, e.ErrorCode); Assert.Contains("UPDATE statement misses WHERE clause", e.InnerException?.Message); // Abort the transaction on the mock server. The transaction should be able to internally retry. _fixture.SpannerMock.AbortTransaction(transaction.TransactionId); if (enableInternalRetries) { await transaction.CommitAsync(); Assert.Equal(1, transaction.RetryCount); } else { var se = await Assert.ThrowsAsync <SpannerException>(() => transaction.CommitAsync()); Assert.Equal(ErrorCode.Aborted, se.ErrorCode); } }
public async Task ReadWriteTransaction_BatchDmlWithSameExceptionHalfwayAndSameResults_CanBeRetried(bool enableInternalRetries) { string sql1 = $"UPDATE Foo SET Bar='valid' WHERE Id={_fixture.RandomLong()}"; string sql2 = $"UPDATE Foo SET Bar='invalid' Id={_fixture.RandomLong()}"; _fixture.SpannerMock.AddOrUpdateStatementResult(sql1, StatementResult.CreateUpdateCount(1)); _fixture.SpannerMock.AddOrUpdateStatementResult(sql2, StatementResult.CreateException(new RpcException(new Status(StatusCode.FailedPrecondition, "UPDATE statement misses WHERE clause")))); using var connection = CreateConnection(); await connection.OpenAsync(); using var transaction = await connection.BeginTransactionAsync(); transaction.EnableInternalRetries = enableInternalRetries; var cmd = connection.CreateBatchDmlCommand(); cmd.Transaction = transaction; cmd.Add(sql1); cmd.Add(sql2); var e = await Assert.ThrowsAsync <SpannerBatchNonQueryException>(() => cmd.ExecuteNonQueryAsync()); Assert.Contains("UPDATE statement misses WHERE clause", e.Message); Assert.Equal(new List <long> { 1 }, e.SuccessfulCommandResults); // Abort the transaction and try to commit. That will trigger a retry, and the retry will receive // the same error and the same results for the BatchDML call as the original attempt. _fixture.SpannerMock.AbortTransaction(transaction.TransactionId); if (enableInternalRetries) { await transaction.CommitAsync(); Assert.Equal(1, transaction.RetryCount); } else { var se = await Assert.ThrowsAsync <SpannerException>(() => transaction.CommitAsync()); Assert.Equal(ErrorCode.Aborted, se.ErrorCode); } }
public async Task ReadWriteTransaction_QueryWithSameException_CanBeRetried(bool enableInternalRetries) { string sql = $"SELECT Id FROM Foo WHERE Id={_fixture.RandomLong()}"; _fixture.SpannerMock.AddOrUpdateStatementResult(sql, StatementResult.CreateException(new RpcException(new Status(StatusCode.NotFound, "Table not found: Foo")))); using var connection = CreateConnection(); await connection.OpenAsync(); using var transaction = await connection.BeginTransactionAsync(); transaction.EnableInternalRetries = enableInternalRetries; var cmd = connection.CreateSelectCommand(sql); cmd.Transaction = transaction; using (var reader = await cmd.ExecuteReaderAsync()) { // Any query error is thrown by the first call to reader.ReadAsync(); var e = await Assert.ThrowsAsync <SpannerException>(() => reader.ReadAsync()); Assert.Equal(ErrorCode.NotFound, e.ErrorCode); Assert.Contains("Table not found: Foo", e.InnerException.Message); } // Abort the transaction on the mock server. The transaction should be able to internally retry. _fixture.SpannerMock.AbortTransaction(transaction.TransactionId); if (enableInternalRetries) { await transaction.CommitAsync(); Assert.Equal(1, transaction.RetryCount); } else { var se = await Assert.ThrowsAsync <SpannerException>(() => transaction.CommitAsync()); Assert.Equal(ErrorCode.Aborted, se.ErrorCode); } }