@@ -1294,6 +1294,204 @@ public void testMutationOnlyUsingAsyncTransactionManager() {
12941294        request .getPrecommitToken ().getPrecommitToken ());
12951295  }
12961296
1297+   private  Spanner  setupSpannerForAbortedBeginTransactionTests () {
1298+     // Force the BeginTransaction RPC to return Aborted the first time it is called. The exception 
1299+     // is cleared after the first call, so the retry should succeed. 
1300+     mockSpanner .setBeginTransactionExecutionTime (
1301+         SimulatedExecutionTime .ofException (
1302+             mockSpanner .createAbortedException (ByteString .copyFromUtf8 ("test" ))));
1303+ 
1304+     return  SpannerOptions .newBuilder ()
1305+         .setProjectId ("test-project" )
1306+         .setChannelProvider (channelProvider )
1307+         .setCredentials (NoCredentials .getInstance ())
1308+         .setSessionPoolOption (
1309+             SessionPoolOptions .newBuilder ()
1310+                 .setUseMultiplexedSession (true )
1311+                 .setUseMultiplexedSessionForRW (true )
1312+                 .setSkipVerifyingBeginTransactionForMuxRW (true )
1313+                 .build ())
1314+         .build ()
1315+         .getService ();
1316+   }
1317+ 
1318+   private  void  verifyMutationKeySetInBeginTransactionRequests (
1319+       List <BeginTransactionRequest > beginTransactionRequests ) {
1320+     assertEquals (2 , beginTransactionRequests .size ());
1321+     // Verify the requests are executed using multiplexed sessions 
1322+     for  (BeginTransactionRequest  request  : beginTransactionRequests ) {
1323+       assertTrue (mockSpanner .getSession (request .getSession ()).getMultiplexed ());
1324+       assertTrue (request .hasMutationKey ());
1325+       assertTrue (request .getMutationKey ().hasInsert ());
1326+     }
1327+   }
1328+ 
1329+   private  void  verifyPreCommitTokenSetInCommitRequest (List <CommitRequest > commitRequests ) {
1330+     assertEquals (1L , commitRequests .size ());
1331+     for  (CommitRequest  request  : commitRequests ) {
1332+       assertTrue (mockSpanner .getSession (request .getSession ()).getMultiplexed ());
1333+       assertNotNull (request .getPrecommitToken ());
1334+       assertEquals (
1335+           ByteString .copyFromUtf8 ("TransactionPrecommitToken" ),
1336+           request .getPrecommitToken ().getPrecommitToken ());
1337+     }
1338+   }
1339+ 
1340+   // The following 4 tests validate mutation-only cases where the BeginTransaction RPC fails with an 
1341+   // ABORTED or retryable error 
1342+   @ Test 
1343+   public  void  testMutationOnlyCaseAbortedDuringBeginTransaction () {
1344+     // This test ensures that when a transaction containing only mutations is retried after an 
1345+     // ABORT error in the BeginTransaction RPC: 
1346+     // 1. The mutation key is correctly included in the BeginTransaction request. 
1347+     // 2. The precommit token is properly set in the Commit request. 
1348+     Spanner  spanner  = setupSpannerForAbortedBeginTransactionTests ();
1349+     DatabaseClientImpl  client  =
1350+         (DatabaseClientImpl ) spanner .getDatabaseClient (DatabaseId .of ("p" , "i" , "d" ));
1351+ 
1352+     client 
1353+         .readWriteTransaction ()
1354+         .run (
1355+             transaction  -> {
1356+               Mutation  mutation  =
1357+                   Mutation .newInsertBuilder ("FOO" ).set ("ID" ).to (1L ).set ("NAME" ).to ("Bar" ).build ();
1358+               transaction .buffer (mutation );
1359+               return  null ;
1360+             });
1361+ 
1362+     // Verify that for mutation only case, a mutation key is set in BeginTransactionRequest. 
1363+     List <BeginTransactionRequest > beginTransactionRequests  =
1364+         mockSpanner .getRequestsOfType (BeginTransactionRequest .class );
1365+     verifyMutationKeySetInBeginTransactionRequests (beginTransactionRequests );
1366+ 
1367+     // Verify that the latest precommit token is set in the CommitRequest 
1368+     List <CommitRequest > commitRequests  = mockSpanner .getRequestsOfType (CommitRequest .class );
1369+     verifyPreCommitTokenSetInCommitRequest (commitRequests );
1370+ 
1371+     spanner .close ();
1372+   }
1373+ 
1374+   @ Test 
1375+   public  void  testMutationOnlyUsingTransactionManagerAbortedDuringBeginTransaction () {
1376+     // This test ensures that when a transaction containing only mutations is retried after an 
1377+     // ABORT error in the BeginTransaction RPC: 
1378+     // 1. The mutation key is correctly included in the BeginTransaction request. 
1379+     // 2. The precommit token is properly set in the Commit request. 
1380+     Spanner  spanner  = setupSpannerForAbortedBeginTransactionTests ();
1381+     DatabaseClientImpl  client  =
1382+         (DatabaseClientImpl ) spanner .getDatabaseClient (DatabaseId .of ("p" , "i" , "d" ));
1383+ 
1384+     try  (TransactionManager  manager  = client .transactionManager ()) {
1385+       TransactionContext  transaction  = manager .begin ();
1386+       while  (true ) {
1387+         try  {
1388+           Mutation  mutation  =
1389+               Mutation .newInsertBuilder ("FOO" ).set ("ID" ).to (1L ).set ("NAME" ).to ("Bar" ).build ();
1390+           transaction .buffer (mutation );
1391+           manager .commit ();
1392+           assertNotNull (manager .getCommitTimestamp ());
1393+           break ;
1394+         } catch  (AbortedException  e ) {
1395+           transaction  = manager .resetForRetry ();
1396+         }
1397+       }
1398+     }
1399+ 
1400+     // Verify that for mutation only case, a mutation key is set in BeginTransactionRequest. 
1401+     List <BeginTransactionRequest > beginTransactionRequests  =
1402+         mockSpanner .getRequestsOfType (BeginTransactionRequest .class );
1403+     verifyMutationKeySetInBeginTransactionRequests (beginTransactionRequests );
1404+ 
1405+     // Verify that the latest precommit token is set in the CommitRequest 
1406+     List <CommitRequest > commitRequests  = mockSpanner .getRequestsOfType (CommitRequest .class );
1407+     verifyPreCommitTokenSetInCommitRequest (commitRequests );
1408+ 
1409+     spanner .close ();
1410+   }
1411+ 
1412+   @ Test 
1413+   public  void  testMutationOnlyUsingAsyncRunnerAbortedDuringBeginTransaction () {
1414+     // This test ensures that when a transaction containing only mutations is retried after an 
1415+     // ABORT error in the BeginTransaction RPC: 
1416+     // 1. The mutation key is correctly included in the BeginTransaction request. 
1417+     // 2. The precommit token is properly set in the Commit request. 
1418+ 
1419+     Spanner  spanner  = setupSpannerForAbortedBeginTransactionTests ();
1420+     DatabaseClientImpl  client  =
1421+         (DatabaseClientImpl ) spanner .getDatabaseClient (DatabaseId .of ("p" , "i" , "d" ));
1422+ 
1423+     AsyncRunner  runner  = client .runAsync ();
1424+     get (
1425+         runner .runAsync (
1426+             txn  -> {
1427+               txn .buffer (
1428+                   Mutation .newInsertBuilder ("FOO" ).set ("ID" ).to (1L ).set ("NAME" ).to ("Bar" ).build ());
1429+               return  ApiFutures .immediateFuture (null );
1430+             },
1431+             MoreExecutors .directExecutor ()));
1432+ 
1433+     // Verify that for mutation only case, a mutation key is set in BeginTransactionRequest. 
1434+     List <BeginTransactionRequest > beginTransactionRequests  =
1435+         mockSpanner .getRequestsOfType (BeginTransactionRequest .class );
1436+     verifyMutationKeySetInBeginTransactionRequests (beginTransactionRequests );
1437+ 
1438+     // Verify that the latest precommit token is set in the CommitRequest 
1439+     List <CommitRequest > commitRequests  = mockSpanner .getRequestsOfType (CommitRequest .class );
1440+     verifyPreCommitTokenSetInCommitRequest (commitRequests );
1441+ 
1442+     spanner .close ();
1443+   }
1444+ 
1445+   @ Test 
1446+   public  void  testMutationOnlyUsingTransactionManagerAsyncAbortedDuringBeginTransaction ()
1447+       throws  Exception  {
1448+     // This test verifies that in the case of mutations-only, when a transaction is retried after an 
1449+     // ABORT in BeginTransaction RPC, the mutation key is correctly set in the BeginTransaction 
1450+     // request 
1451+     // and precommit token is set in Commit request. 
1452+     Spanner  spanner  = setupSpannerForAbortedBeginTransactionTests ();
1453+     DatabaseClientImpl  client  =
1454+         (DatabaseClientImpl ) spanner .getDatabaseClient (DatabaseId .of ("p" , "i" , "d" ));
1455+ 
1456+     try  (AsyncTransactionManager  manager  = client .transactionManagerAsync ()) {
1457+       TransactionContextFuture  transaction  = manager .beginAsync ();
1458+       while  (true ) {
1459+         CommitTimestampFuture  commitTimestamp  =
1460+             transaction 
1461+                 .then (
1462+                     (txn , input ) -> {
1463+                       txn .buffer (
1464+                           Mutation .newInsertBuilder ("FOO" )
1465+                               .set ("ID" )
1466+                               .to (1L )
1467+                               .set ("NAME" )
1468+                               .to ("Bar" )
1469+                               .build ());
1470+                       return  ApiFutures .immediateFuture (null );
1471+                     },
1472+                     MoreExecutors .directExecutor ())
1473+                 .commitAsync ();
1474+         try  {
1475+           assertThat (commitTimestamp .get ()).isNotNull ();
1476+           break ;
1477+         } catch  (AbortedException  e ) {
1478+           transaction  = manager .resetForRetryAsync ();
1479+         }
1480+       }
1481+     }
1482+ 
1483+     // Verify that for mutation only case, a mutation key is set in BeginTransactionRequest. 
1484+     List <BeginTransactionRequest > beginTransactionRequests  =
1485+         mockSpanner .getRequestsOfType (BeginTransactionRequest .class );
1486+     verifyMutationKeySetInBeginTransactionRequests (beginTransactionRequests );
1487+ 
1488+     // Verify that the latest precommit token is set in the CommitRequest 
1489+     List <CommitRequest > commitRequests  = mockSpanner .getRequestsOfType (CommitRequest .class );
1490+     verifyPreCommitTokenSetInCommitRequest (commitRequests );
1491+ 
1492+     spanner .close ();
1493+   }
1494+ 
12971495  // Tests the behavior of the server-side kill switch for read-write multiplexed sessions.. 
12981496  @ Test 
12991497  public  void  testInitialBeginTransactionWithRW_receivesUnimplemented_fallsBackToRegularSession () {
0 commit comments