diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java index 90f5317e88..3d6a015531 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java @@ -107,6 +107,7 @@ public void onSuccess(AsyncTransactionManagerImpl result) { new ApiFutureCallback() { @Override public void onFailure(Throwable t) { + session.close(); res.setException(t); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java index 2449b8fba7..371d3688c9 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; @@ -1084,6 +1085,37 @@ public void onSuccess(Long aLong) { } } + @Test + public void testAbandonedAsyncTransactionManager_rollbackFails() throws Exception { + mockSpanner.setRollbackExecutionTime( + SimulatedExecutionTime.ofException(Status.PERMISSION_DENIED.asRuntimeException())); + + boolean gotException = false; + try (AsyncTransactionManager manager = client().transactionManagerAsync()) { + TransactionContextFuture transactionContextFuture = manager.beginAsync(); + while (true) { + try { + AsyncTransactionStep updateCount = + transactionContextFuture.then( + (transactionContext, ignored) -> + transactionContext.executeUpdateAsync(UPDATE_STATEMENT), + executor); + assertEquals(1L, updateCount.get().longValue()); + // Break without committing or rolling back the transaction. + break; + } catch (AbortedException e) { + transactionContextFuture = manager.resetForRetryAsync(); + } + } + } catch (SpannerException spannerException) { + // The error from the automatically executed Rollback is surfaced when the + // AsyncTransactionManager is closed. + assertEquals(ErrorCode.PERMISSION_DENIED, spannerException.getErrorCode()); + gotException = true; + } + assertTrue(gotException); + } + private boolean isMultiplexedSessionsEnabled() { if (spanner.getOptions() == null || spanner.getOptions().getSessionPoolOptions() == null) { return false;