@@ -2010,6 +2010,62 @@ public void testBatchWriteAtLeastOnce() {
20102010 mockSpanner .getSession (beginTransactionRequests .get (1 ).getSession ()).getMultiplexed ());
20112011 }
20122012
2013+ @ Test
2014+ public void
2015+ testReadWriteUnimplementedError_RetriedWithRegularSessionForInFlightTransaction_FailsWithSessionNotFound () {
2016+ // Test scenario:
2017+ // 1. The initial attempt performs an inline begin using a multiplexed session, but with the
2018+ // backend flag assumed to be OFF, resulting in an UNIMPLEMENTED error.
2019+ // 2. Upon encountering the UNIMPLEMENTED error, the entire transaction callable is retried
2020+ // using regular sessions. However, the Commit request fails due to a SessionNotFound error.
2021+ // 3. A final retry is triggered to handle the SessionNotFound error by selecting a new session
2022+ // from the pool, leading to a successful transaction.
2023+ Spanner spanner = setupSpannerForAbortedBeginTransactionTests ();
2024+
2025+ // The first ExecuteSql request that does an inline begin with multiplexed sessions fail with
2026+ // UNIMPLEMENTED error.
2027+ mockSpanner .setExecuteSqlExecutionTime (
2028+ SimulatedExecutionTime .ofException (
2029+ Status .UNIMPLEMENTED
2030+ .withDescription (
2031+ "Transaction type read_write not supported with multiplexed sessions" )
2032+ .asRuntimeException ()));
2033+
2034+ // The first Commit request fails with SessionNotFound exception. The first time this commit is
2035+ // called with be using regular sessions.
2036+ // This is done to verify if SessionNotFound errors on regular sessions are handled.
2037+ mockSpanner .setCommitExecutionTime (
2038+ SimulatedExecutionTime .ofException (
2039+ mockSpanner .createSessionNotFoundException ("TEST_SESSION_NAME" )));
2040+
2041+ DatabaseClientImpl client =
2042+ (DatabaseClientImpl ) spanner .getDatabaseClient (DatabaseId .of ("p" , "i" , "d" ));
2043+ TransactionRunner runner = client .readWriteTransaction ();
2044+ Long updateCount =
2045+ runner .run (
2046+ transaction -> {
2047+ return transaction .executeUpdate (UPDATE_STATEMENT );
2048+ });
2049+
2050+ assertThat (updateCount ).isEqualTo (1L );
2051+ List <ExecuteSqlRequest > executeSqlRequests =
2052+ mockSpanner .getRequestsOfType (ExecuteSqlRequest .class );
2053+ assertEquals (3 , executeSqlRequests .size ());
2054+
2055+ // Verify the first BeginTransaction request is executed using multiplexed sessions.
2056+ assertTrue (mockSpanner .getSession (executeSqlRequests .get (0 ).getSession ()).getMultiplexed ());
2057+
2058+ // Verify the second BeginTransaction request is executed using regular sessions.
2059+ assertFalse (mockSpanner .getSession (executeSqlRequests .get (1 ).getSession ()).getMultiplexed ());
2060+
2061+ // Verify the second BeginTransaction request is executed using regular sessions.
2062+ assertFalse (mockSpanner .getSession (executeSqlRequests .get (2 ).getSession ()).getMultiplexed ());
2063+
2064+ // Verify that after the first regular session failed with SessionNotFoundException, a new
2065+ // regular session is picked up to re-run the transaction.
2066+ assertNotEquals (executeSqlRequests .get (1 ).getSession (), executeSqlRequests .get (2 ).getSession ());
2067+ }
2068+
20132069 private void waitForSessionToBeReplaced (DatabaseClientImpl client ) {
20142070 assertNotNull (client .multiplexedSessionDatabaseClient );
20152071 SessionReference sessionReference =
0 commit comments