Skip to content

fix: getCommitResponse() should return error if tx rolled back #4021

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,12 @@ interface AsyncTransactionFunction<I, O> {
/** Returns the state of the transaction. */
TransactionState getState();

/** Returns the {@link CommitResponse} of this transaction. */
/**
* Returns an {@link ApiFuture} with the eventual {@link CommitResponse} of this transaction. This
* method can be called before the transaction has been committed. The {@link ApiFuture} will be
* done when the transaction is committed or rolled back. If the commit fails or the transaction
* is rolled back, the result of this {@link ApiFuture} will be an exception.
*/
ApiFuture<CommitResponse> getCommitResponse();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ public ApiFuture<Void> rollbackAsync() {
Preconditions.checkState(
txnState == TransactionState.STARTED,
"rollback can only be called if the transaction is in progress");
if (!commitResponse.isDone()) {
commitResponse.setException(
SpannerExceptionFactory.newSpannerException(
ErrorCode.FAILED_PRECONDITION, "The transaction has been rolled back"));
}
try {
return ApiFutures.transformAsync(
txn.rollbackAsync(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import static com.google.cloud.spanner.SpannerApiFutures.get;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
Expand Down Expand Up @@ -200,6 +201,39 @@ public void asyncTransactionManager_shouldRollbackOnCloseAsync() throws Exceptio
0L);
}

@Test
public void testAsyncTransactionManager_commitResponseReturnsErrorAfterRollback()
throws Exception {
try (AsyncTransactionManager manager = client().transactionManagerAsync()) {
TransactionContextFuture transactionContextFuture = manager.beginAsync();
ApiFuture<CommitResponse> commitResponse = manager.getCommitResponse();
while (true) {
try {
AsyncTransactionStep<Void, ?> next =
transactionContextFuture.then(
(transactionContext, ignored) ->
transactionContext.bufferAsync(
Collections.singleton(Mutation.delete("FOO", Key.of("foo")))),
executor);
assertFalse(commitResponse.isDone());
manager.rollbackAsync().get();
assertTrue(commitResponse.isDone());
ExecutionException executionException =
assertThrows(ExecutionException.class, commitResponse::get);
assertEquals(SpannerException.class, executionException.getCause().getClass());
SpannerException spannerException = (SpannerException) executionException.getCause();
assertEquals(ErrorCode.FAILED_PRECONDITION, spannerException.getErrorCode());
assertEquals(
"FAILED_PRECONDITION: The transaction has been rolled back",
spannerException.getMessage());
break;
} catch (AbortedException e) {
transactionContextFuture = manager.resetForRetryAsync();
}
}
}
}

@Test
public void testAsyncTransactionManager_returnsCommitStats() throws Exception {
try (AsyncTransactionManager manager =
Expand Down
Loading