From 046299bf743cd2b4a69b264f3adf051037f4ddd6 Mon Sep 17 00:00:00 2001 From: Sri Harsha CH Date: Thu, 2 Jan 2025 10:33:28 +0000 Subject: [PATCH 1/3] chore(Spanner): fix tests for mux rw --- .../MultiplexedSessionDatabaseClient.java | 13 +- .../cloud/spanner/SessionPoolOptions.java | 31 +++- .../cloud/spanner/DatabaseClientImplTest.java | 63 +++++-- .../spanner/InlineBeginTransactionTest.java | 7 + .../cloud/spanner/MockSpannerServiceImpl.java | 27 ++- .../spanner/OpenTelemetryApiTracerTest.java | 2 + ...OpenTelemetryBuiltInMetricsTracerTest.java | 1 + .../RetryOnInvalidatedSessionTest.java | 174 ++++++++++++++++++ .../cloud/spanner/SessionPoolLeakTest.java | 19 ++ .../spanner/TransactionChannelHintTest.java | 2 + .../spanner/TransactionManagerImplTest.java | 30 +++ .../spanner/TransactionRunnerImplTest.java | 15 ++ 12 files changed, 355 insertions(+), 29 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClient.java index 01f41a2dfdc..89371a21c51 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClient.java @@ -253,10 +253,15 @@ public void onSessionReady(SessionImpl session) { // initiate a begin transaction request to verify if read-write transactions are // supported using multiplexed sessions. if (sessionClient - .getSpanner() - .getOptions() - .getSessionPoolOptions() - .getUseMultiplexedSessionForRW()) { + .getSpanner() + .getOptions() + .getSessionPoolOptions() + .getUseMultiplexedSessionForRW() + && !sessionClient + .getSpanner() + .getOptions() + .getSessionPoolOptions() + .getSkipVerifyBeginTransactionForMuxRW()) { verifyBeginTransactionWithRWOnMultiplexedSessionAsync(session.getName()); } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java index 36a4e5fe208..03551640b43 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java @@ -83,6 +83,7 @@ public class SessionPoolOptions { // TODO: Change to use java.time.Duration. private final Duration multiplexedSessionMaintenanceDuration; + private final boolean skipVerifyingBeginTransactionForMuxRW; private SessionPoolOptions(Builder builder) { // minSessions > maxSessions is only possible if the user has only set a value for maxSessions. @@ -132,6 +133,7 @@ private SessionPoolOptions(Builder builder) { ? useMultiplexedSessionFromEnvVariablePartitionedOps : builder.useMultiplexedSessionPartitionedOps; this.multiplexedSessionMaintenanceDuration = builder.multiplexedSessionMaintenanceDuration; + this.skipVerifyingBeginTransactionForMuxRW = builder.skipVerifyingBeginTransactionForMuxRW; } @Override @@ -169,8 +171,10 @@ public boolean equals(Object o) { && Objects.equals(this.useMultiplexedSession, other.useMultiplexedSession) && Objects.equals(this.useMultiplexedSessionForRW, other.useMultiplexedSessionForRW) && Objects.equals( - this.multiplexedSessionMaintenanceDuration, - other.multiplexedSessionMaintenanceDuration); + this.multiplexedSessionMaintenanceDuration, other.multiplexedSessionMaintenanceDuration) + && Objects.equals( + this.skipVerifyingBeginTransactionForMuxRW, + other.skipVerifyingBeginTransactionForMuxRW); } @Override @@ -199,7 +203,8 @@ public int hashCode() { this.poolMaintainerClock, this.useMultiplexedSession, this.useMultiplexedSessionForRW, - this.multiplexedSessionMaintenanceDuration); + this.multiplexedSessionMaintenanceDuration, + this.skipVerifyingBeginTransactionForMuxRW); } public Builder toBuilder() { @@ -392,6 +397,12 @@ Duration getMultiplexedSessionMaintenanceDuration() { return multiplexedSessionMaintenanceDuration; } + @VisibleForTesting + @InternalApi + boolean getSkipVerifyBeginTransactionForMuxRW() { + return skipVerifyingBeginTransactionForMuxRW; + } + public static Builder newBuilder() { return new Builder(); } @@ -607,6 +618,7 @@ public static class Builder { private Duration multiplexedSessionMaintenanceDuration = Duration.ofDays(7); private Clock poolMaintainerClock = Clock.INSTANCE; + private boolean skipVerifyingBeginTransactionForMuxRW = false; private static Position getReleaseToPositionFromSystemProperty() { // NOTE: This System property is a beta feature. Support for it can be removed in the future. @@ -650,6 +662,7 @@ private Builder(SessionPoolOptions options) { this.useMultiplexedSessionPartitionedOps = options.useMultiplexedSessionForPartitionedOps; this.multiplexedSessionMaintenanceDuration = options.multiplexedSessionMaintenanceDuration; this.poolMaintainerClock = options.poolMaintainerClock; + this.skipVerifyingBeginTransactionForMuxRW = options.skipVerifyingBeginTransactionForMuxRW; } /** @@ -872,6 +885,18 @@ Builder setMultiplexedSessionMaintenanceDuration( return this; } + // The additional BeginTransaction RPC for multiplexed session read-write is causing + // unexpected behavior in mock Spanner tests that rely on mocking the BeginTransaction RPC. + // Invoking this method with `true` skips sending the BeginTransaction RPC when the multiplexed + // session is created for the first time during client initialization. + // This is only used for tests. + @VisibleForTesting + Builder setSkipVerifyingBeginTransactionForMuxRW( + boolean skipVerifyingBeginTransactionForMuxRW) { + this.skipVerifyingBeginTransactionForMuxRW = skipVerifyingBeginTransactionForMuxRW; + return this; + } + /** * Sets whether the client should automatically execute a background query to detect the dialect * that is used by the database or not. Set this option to true if you do not know what the diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java index 86d0bfc2c94..acba7b2b891 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java @@ -270,6 +270,9 @@ public void tearDown() { @Test public void testPoolMaintainer_whenInactiveTransactionAndSessionIsNotFoundOnBackend_removeSessionsFromPool() { + assumeFalse( + "Session pool maintainer test skipped for multiplexed sessions", + isMultiplexedSessionsEnabledForRW()); FakeClock poolMaintainerClock = new FakeClock(); InactiveTransactionRemovalOptions inactiveTransactionRemovalOptions = InactiveTransactionRemovalOptions.newBuilder() @@ -347,6 +350,9 @@ public void tearDown() { @Test public void testPoolMaintainer_whenInactiveTransactionAndSessionExistsOnBackend_removeSessionsFromPool() { + assumeFalse( + "Session leaks tests are skipped for multiplexed sessions", + isMultiplexedSessionsEnabledForRW()); FakeClock poolMaintainerClock = new FakeClock(); InactiveTransactionRemovalOptions inactiveTransactionRemovalOptions = InactiveTransactionRemovalOptions.newBuilder() @@ -482,6 +488,9 @@ public void testPoolMaintainer_whenLongRunningPartitionedUpdateRequest_takeNoAct */ @Test public void testPoolMaintainer_whenPDMLFollowedByInactiveTransaction_removeSessionsFromPool() { + assumeFalse( + "Session leaks tests are skipped for multiplexed sessions", + isMultiplexedSessionsEnabledForRW()); FakeClock poolMaintainerClock = new FakeClock(); InactiveTransactionRemovalOptions inactiveTransactionRemovalOptions = InactiveTransactionRemovalOptions.newBuilder() @@ -3084,14 +3093,14 @@ public void testDatabaseOrInstanceDoesNotExistOnCreate() { .readWriteTransaction() .run(transaction -> transaction.executeUpdate(UPDATE_STATEMENT))); // No additional requests should have been sent by the client. - // Note that in case of the use of multiplexed sessions, then we have 2 requests: + // Note that in case of the use of regular sessions, then we have 1 request: // 1. BatchCreateSessions for the session pool. - // 2. CreateSession for the multiplexed session. - assertThat(mockSpanner.getRequests()) - .hasSize( - spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSession() - ? 2 - : 1); + // Note that in case of the use of multiplexed sessions for read-only and read-write, + // then we have 1 request: + // 1. CreateSession for the multiplexed session. + // There will be no BatchCreateSessions request in case of multiplexed sessions, because + // the session pool options has min size of 0. + assertThat(mockSpanner.getRequests()).hasSize(1); } } mockSpanner.reset(); @@ -3211,9 +3220,16 @@ public void testDatabaseOrInstanceIsDeletedAndThenRecreated() throws Exception { ResourceNotFoundException.class, () -> dbClient.singleUse().executeQuery(SELECT1)); } - assertThrows( - ResourceNotFoundException.class, - () -> dbClient.readWriteTransaction().run(transaction -> null)); + if (!spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()) { + // We only verify this for read-write transactions if we are not using multiplexed + // sessions. For multiplexed sessions, we don't need any special handling, as deleting the + // database will also invalidate the multiplexed session, and trying to continue to use it + // will continue to return an error. + assertThrows( + ResourceNotFoundException.class, + () -> dbClient.readWriteTransaction().run(transaction -> null)); + } + assertThat(mockSpanner.getRequests()).isEmpty(); // Now get a new database client. Normally multiple calls to Spanner#getDatabaseClient will // return the same instance, but not when the instance has been invalidated by a @@ -3300,13 +3316,18 @@ public void testAllowNestedTransactions() throws InterruptedException { Thread.sleep(1L); } assertThat(client.pool.getNumberOfSessionsInPool()).isEqualTo(minSessions); + int expectedMinSessions = + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW() + ? minSessions + : minSessions - 1; Long res = client .readWriteTransaction() .allowNestedTransaction() .run( transaction -> { - assertThat(client.pool.getNumberOfSessionsInPool()).isEqualTo(minSessions - 1); + assertThat(client.pool.getNumberOfSessionsInPool()) + .isEqualTo(expectedMinSessions); return transaction.executeUpdate(UPDATE_STATEMENT); }); assertThat(res).isEqualTo(UPDATE_COUNT); @@ -3333,6 +3354,9 @@ public void testNestedTransactionsUsingTwoDatabases() throws InterruptedExceptio } assertThat(client1.pool.getNumberOfSessionsInPool()).isEqualTo(minSessions); assertThat(client2.pool.getNumberOfSessionsInPool()).isEqualTo(minSessions); + // When read-write transaction uses multiplexed sessions, then sessions are not checked out from + // the session pool. + int expectedMinSessions = isMultiplexedSessionsEnabledForRW() ? minSessions : minSessions - 1; Long res = client1 .readWriteTransaction() @@ -3341,7 +3365,8 @@ public void testNestedTransactionsUsingTwoDatabases() throws InterruptedExceptio transaction -> { // Client1 should have 1 session checked out. // Client2 should have 0 sessions checked out. - assertThat(client1.pool.getNumberOfSessionsInPool()).isEqualTo(minSessions - 1); + assertThat(client1.pool.getNumberOfSessionsInPool()) + .isEqualTo(expectedMinSessions); assertThat(client2.pool.getNumberOfSessionsInPool()).isEqualTo(minSessions); Long add = client2 @@ -3350,9 +3375,9 @@ public void testNestedTransactionsUsingTwoDatabases() throws InterruptedExceptio transaction1 -> { // Both clients should now have 1 session checked out. assertThat(client1.pool.getNumberOfSessionsInPool()) - .isEqualTo(minSessions - 1); + .isEqualTo(expectedMinSessions); assertThat(client2.pool.getNumberOfSessionsInPool()) - .isEqualTo(minSessions - 1); + .isEqualTo(expectedMinSessions); try (ResultSet rs = transaction1.executeQuery(SELECT1)) { if (rs.next()) { return rs.getLong(0); @@ -5090,6 +5115,9 @@ public void testRetryOnResourceExhausted() { @Test public void testSessionPoolExhaustedError_containsStackTraces() { + assumeFalse( + "Session pool tests are skipped for multiplexed sessions", + isMultiplexedSessionsEnabledForRW()); try (Spanner spanner = SpannerOptions.newBuilder() .setProjectId(TEST_PROJECT) @@ -5450,4 +5478,11 @@ private boolean isMultiplexedSessionsEnabled() { } return spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSession(); } + + private boolean isMultiplexedSessionsEnabledForRW() { + if (spanner.getOptions() == null || spanner.getOptions().getSessionPoolOptions() == null) { + return false; + } + return spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW(); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java index c67c0084674..94b6de149c3 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java @@ -191,6 +191,13 @@ public void setUp() { .setChannelProvider(channelProvider) .setCredentials(NoCredentials.getInstance()) .setTrackTransactionStarter() + // The extra BeginTransaction RPC for multiplexed session read-write is causing + // unexpected behavior in tests having a mock on the BeginTransaction RPC. Therefore, + // this is being skipped. + .setSessionPoolOption( + SessionPoolOptions.newBuilder() + .setSkipVerifyingBeginTransactionForMuxRW(true) + .build()) .build() .getService(); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java index 676cb05eb07..9f27b28d323 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java @@ -62,6 +62,7 @@ import com.google.spanner.v1.PartitionReadRequest; import com.google.spanner.v1.PartitionResponse; import com.google.spanner.v1.ReadRequest; +import com.google.spanner.v1.RequestOptions; import com.google.spanner.v1.ResultSet; import com.google.spanner.v1.ResultSetMetadata; import com.google.spanner.v1.ResultSetStats; @@ -1829,7 +1830,7 @@ private ByteString getTransactionId(Session session, TransactionSelector tx) { transactionId = null; break; case BEGIN: - transactionId = beginTransaction(session, tx.getBegin(), null).getId(); + transactionId = beginTransaction(session, tx.getBegin(), null, null).getId(); break; case ID: Transaction transaction = transactions.get(tx.getId()); @@ -1895,7 +1896,8 @@ public void beginTransaction( beginTransactionExecutionTime.simulateExecutionTime( exceptions, stickyGlobalExceptions, freezeLock); Transaction transaction = - beginTransaction(session, request.getOptions(), request.getMutationKey()); + beginTransaction( + session, request.getOptions(), request.getMutationKey(), request.getRequestOptions()); responseObserver.onNext(transaction); responseObserver.onCompleted(); } catch (StatusRuntimeException t) { @@ -1906,7 +1908,10 @@ public void beginTransaction( } private Transaction beginTransaction( - Session session, TransactionOptions options, com.google.spanner.v1.Mutation mutationKey) { + Session session, + TransactionOptions options, + com.google.spanner.v1.Mutation mutationKey, + RequestOptions requestOptions) { ByteString transactionId = generateTransactionName(session.getName()); Transaction.Builder builder = Transaction.newBuilder().setId(transactionId); if (options != null && options.getModeCase() == ModeCase.READ_ONLY) { @@ -1920,12 +1925,17 @@ private Transaction beginTransaction( } Transaction transaction = builder.build(); transactions.put(transaction.getId(), transaction); - transactionsStarted.add(transaction.getId()); + // TODO: remove once UNIMPLEMENTED error is not thrown for read-write mux + // Do not consider the transaction if this request was from background thread + if (requestOptions == null + || !requestOptions.getTransactionTag().equals("multiplexed-rw-background-begin-txn")) { + transactionsStarted.add(transaction.getId()); + if (abortNextTransaction.getAndSet(false)) { + markAbortedTransaction(transaction.getId()); + } + } isPartitionedDmlTransaction.put( transaction.getId(), options.getModeCase() == ModeCase.PARTITIONED_DML); - if (abortNextTransaction.getAndSet(false)) { - markAbortedTransaction(transaction.getId()); - } return transaction; } @@ -2025,7 +2035,8 @@ public void commit(CommitRequest request, StreamObserver respons TransactionOptions.newBuilder() .setReadWrite(ReadWrite.getDefaultInstance()) .build(), - null); + null, + request.getRequestOptions()); } else if (request.getTransactionId() != null) { transaction = transactions.get(request.getTransactionId()); Optional aborted = diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetryApiTracerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetryApiTracerTest.java index e4d25f1d9b3..65bb5f5f0d7 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetryApiTracerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetryApiTracerTest.java @@ -135,6 +135,7 @@ public void createSpannerInstance() { SessionPoolOptions.newBuilder() .setWaitForMinSessionsDuration(Duration.ofSeconds(5L)) .setFailOnSessionLeak() + .setSkipVerifyingBeginTransactionForMuxRW(true) .build()) .setEnableApiTracing(true) .build() @@ -428,6 +429,7 @@ public boolean isEnableApiTracing() { SessionPoolOptions.newBuilder() .setWaitForMinSessionsDuration(Duration.ofSeconds(5L)) .setFailOnSessionLeak() + .setSkipVerifyingBeginTransactionForMuxRW(true) .build()) .build() .getService(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetryBuiltInMetricsTracerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetryBuiltInMetricsTracerTest.java index 7a14681d525..98ee700a943 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetryBuiltInMetricsTracerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetryBuiltInMetricsTracerTest.java @@ -144,6 +144,7 @@ public void createSpannerInstance() { SessionPoolOptions.newBuilder() .setWaitForMinSessionsDuration(Duration.ofSeconds(5L)) .setFailOnSessionLeak() + .setSkipVerifyingBeginTransactionForMuxRW(true) .build()) // Setting this to false so that Spanner Options does not register Metrics Tracer // factory again. diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java index f070f154216..5496ad531bf 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java @@ -538,6 +538,9 @@ public void readOnlyTransactionReadRowUsingIndexNonRecoverable() throws Interrup @Test public void readWriteTransactionReadOnlySessionInPool() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); SessionPoolOptions.Builder builder = SessionPoolOptions.newBuilder(); if (failOnInvalidatedSession) { builder.setFailIfSessionNotFound(); @@ -568,6 +571,9 @@ public void readWriteTransactionReadOnlySessionInPool() throws InterruptedExcept @Test public void readWriteTransactionSelect() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); TransactionRunner runner = client.readWriteTransaction(); assertThrowsSessionNotFoundIfShouldFail( () -> @@ -582,6 +588,9 @@ public void readWriteTransactionSelect() throws InterruptedException { @Test public void readWriteTransactionRead() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); TransactionRunner runner = client.readWriteTransaction(); assertThrowsSessionNotFoundIfShouldFail( () -> @@ -597,6 +606,9 @@ public void readWriteTransactionRead() throws InterruptedException { @Test public void readWriteTransactionReadWithOptimisticLock() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); TransactionRunner runner = client.readWriteTransaction(Options.optimisticLock()); assertThrowsSessionNotFoundIfShouldFail( () -> @@ -612,6 +624,9 @@ public void readWriteTransactionReadWithOptimisticLock() throws InterruptedExcep @Test public void readWriteTransactionReadUsingIndex() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); TransactionRunner runner = client.readWriteTransaction(); assertThrowsSessionNotFoundIfShouldFail( () -> @@ -628,6 +643,9 @@ public void readWriteTransactionReadUsingIndex() throws InterruptedException { @Test public void readWriteTransactionReadRow() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); TransactionRunner runner = client.readWriteTransaction(); assertThrowsSessionNotFoundIfShouldFail( () -> @@ -638,6 +656,9 @@ public void readWriteTransactionReadRow() throws InterruptedException { @Test public void readWriteTransactionReadRowUsingIndex() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); TransactionRunner runner = client.readWriteTransaction(); assertThrowsSessionNotFoundIfShouldFail( () -> @@ -649,6 +670,9 @@ public void readWriteTransactionReadRowUsingIndex() throws InterruptedException @Test public void readWriteTransactionUpdate() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); TransactionRunner runner = client.readWriteTransaction(); assertThrowsSessionNotFoundIfShouldFail( () -> runner.run(transaction -> transaction.executeUpdate(UPDATE_STATEMENT))); @@ -656,6 +680,9 @@ public void readWriteTransactionUpdate() throws InterruptedException { @Test public void readWriteTransactionBatchUpdate() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); TransactionRunner runner = client.readWriteTransaction(); assertThrowsSessionNotFoundIfShouldFail( () -> @@ -666,6 +693,9 @@ public void readWriteTransactionBatchUpdate() throws InterruptedException { @Test public void readWriteTransactionBuffer() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); TransactionRunner runner = client.readWriteTransaction(); assertThrowsSessionNotFoundIfShouldFail( () -> @@ -678,6 +708,9 @@ public void readWriteTransactionBuffer() throws InterruptedException { @Test public void readWriteTransactionSelectInvalidatedDuringTransaction() { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); TransactionRunner runner = client.readWriteTransaction(); final AtomicInteger attempt = new AtomicInteger(); assertThrowsSessionNotFoundIfShouldFail( @@ -702,6 +735,9 @@ public void readWriteTransactionSelectInvalidatedDuringTransaction() { @Test public void readWriteTransactionReadInvalidatedDuringTransaction() { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); TransactionRunner runner = client.readWriteTransaction(); final AtomicInteger attempt = new AtomicInteger(); assertThrowsSessionNotFoundIfShouldFail( @@ -728,6 +764,9 @@ public void readWriteTransactionReadInvalidatedDuringTransaction() { @Test public void readWriteTransactionReadUsingIndexInvalidatedDuringTransaction() { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); TransactionRunner runner = client.readWriteTransaction(); final AtomicInteger attempt = new AtomicInteger(); assertThrowsSessionNotFoundIfShouldFail( @@ -756,6 +795,9 @@ public void readWriteTransactionReadUsingIndexInvalidatedDuringTransaction() { @Test public void readWriteTransactionReadRowInvalidatedDuringTransaction() { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); TransactionRunner runner = client.readWriteTransaction(); final AtomicInteger attempt = new AtomicInteger(); assertThrowsSessionNotFoundIfShouldFail( @@ -778,6 +820,9 @@ public void readWriteTransactionReadRowInvalidatedDuringTransaction() { @Test public void readWriteTransactionReadRowUsingIndexInvalidatedDuringTransaction() { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); TransactionRunner runner = client.readWriteTransaction(); final AtomicInteger attempt = new AtomicInteger(); assertThrowsSessionNotFoundIfShouldFail( @@ -803,6 +848,9 @@ public void readWriteTransactionReadRowUsingIndexInvalidatedDuringTransaction() @SuppressWarnings("resource") @Test public void transactionManagerReadOnlySessionInPool() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); try (TransactionManager manager = client.transactionManager()) { TransactionContext transaction = manager.begin(); while (true) { @@ -825,6 +873,9 @@ public void transactionManagerReadOnlySessionInPool() throws InterruptedExceptio @SuppressWarnings("resource") @Test public void transactionManagerSelect() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); try (TransactionManager manager = client.transactionManager()) { TransactionContext transaction = manager.begin(); while (true) { @@ -847,6 +898,9 @@ public void transactionManagerSelect() throws InterruptedException { @SuppressWarnings("resource") @Test public void transactionManagerRead() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); try (TransactionManager manager = client.transactionManager()) { TransactionContext transaction = manager.begin(); while (true) { @@ -870,6 +924,9 @@ public void transactionManagerRead() throws InterruptedException { @SuppressWarnings("resource") @Test public void transactionManagerReadUsingIndex() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); try (TransactionManager manager = client.transactionManager()) { TransactionContext transaction = manager.begin(); while (true) { @@ -893,6 +950,9 @@ public void transactionManagerReadUsingIndex() throws InterruptedException { @Test public void transactionManagerReadRow() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); try (TransactionManager manager = client.transactionManager()) { TransactionContext transaction = manager.begin(); while (true) { @@ -914,6 +974,9 @@ public void transactionManagerReadRow() throws InterruptedException { @Test public void transactionManagerReadRowUsingIndex() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); try (TransactionManager manager = client.transactionManager()) { TransactionContext transaction = manager.begin(); while (true) { @@ -937,6 +1000,9 @@ public void transactionManagerReadRowUsingIndex() throws InterruptedException { @Test public void transactionManagerUpdate() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); try (TransactionManager manager = client.transactionManager(Options.commitStats())) { TransactionContext transaction = manager.begin(); while (true) { @@ -958,6 +1024,9 @@ public void transactionManagerUpdate() throws InterruptedException { @Test public void transactionManagerAborted_thenSessionNotFoundOnBeginTransaction() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); int attempt = 0; try (TransactionManager manager = client.transactionManager()) { TransactionContext transaction = manager.begin(); @@ -990,6 +1059,9 @@ public void transactionManagerAborted_thenSessionNotFoundOnBeginTransaction() @Test public void transactionManagerBatchUpdate() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); try (TransactionManager manager = client.transactionManager()) { TransactionContext transaction = manager.begin(); while (true) { @@ -1013,6 +1085,9 @@ public void transactionManagerBatchUpdate() throws InterruptedException { @SuppressWarnings("resource") @Test public void transactionManagerBuffer() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); try (TransactionManager manager = client.transactionManager()) { TransactionContext transaction = manager.begin(); while (true) { @@ -1037,6 +1112,9 @@ public void transactionManagerBuffer() throws InterruptedException { @SuppressWarnings("resource") @Test public void transactionManagerSelectInvalidatedDuringTransaction() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); SessionPoolOptions.Builder builder = SessionPoolOptions.newBuilder(); if (failOnInvalidatedSession) { builder.setFailIfSessionNotFound(); @@ -1083,6 +1161,9 @@ public void transactionManagerSelectInvalidatedDuringTransaction() throws Interr @SuppressWarnings("resource") @Test public void transactionManagerReadInvalidatedDuringTransaction() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); SessionPoolOptions.Builder builder = SessionPoolOptions.newBuilder(); if (failOnInvalidatedSession) { builder.setFailIfSessionNotFound(); @@ -1131,6 +1212,9 @@ public void transactionManagerReadInvalidatedDuringTransaction() throws Interrup @Test public void transactionManagerReadUsingIndexInvalidatedDuringTransaction() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); SessionPoolOptions.Builder builder = SessionPoolOptions.newBuilder(); if (failOnInvalidatedSession) { builder.setFailIfSessionNotFound(); @@ -1180,6 +1264,9 @@ public void transactionManagerReadUsingIndexInvalidatedDuringTransaction() @SuppressWarnings("resource") @Test public void transactionManagerReadRowInvalidatedDuringTransaction() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); SessionPoolOptions.Builder builder = SessionPoolOptions.newBuilder(); if (failOnInvalidatedSession) { builder.setFailIfSessionNotFound(); @@ -1226,6 +1313,9 @@ public void transactionManagerReadRowInvalidatedDuringTransaction() throws Inter @Test public void transactionManagerReadRowUsingIndexInvalidatedDuringTransaction() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); SessionPoolOptions.Builder builder = SessionPoolOptions.newBuilder(); if (failOnInvalidatedSession) { builder.setFailIfSessionNotFound(); @@ -1283,6 +1373,9 @@ public void partitionedDml() throws InterruptedException { @Test public void write() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); assertThrowsSessionNotFoundIfShouldFail( () -> client.write(Collections.singletonList(Mutation.delete("FOO", KeySet.all())))); } @@ -1300,17 +1393,26 @@ public void writeAtLeastOnce() throws InterruptedException { @Test public void asyncRunnerSelect() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncRunner_withReadFunction(input -> input.executeQueryAsync(SELECT1AND2)); } @Test public void asyncRunnerRead() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncRunner_withReadFunction( input -> input.readAsync("FOO", KeySet.all(), Collections.singletonList("BAR"))); } @Test public void asyncRunnerReadUsingIndex() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncRunner_withReadFunction( input -> input.readUsingIndexAsync( @@ -1319,6 +1421,9 @@ public void asyncRunnerReadUsingIndex() throws InterruptedException { private void asyncRunner_withReadFunction( final Function readFunction) throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); final ExecutorService queryExecutor = Executors.newSingleThreadExecutor(); try { AsyncRunner runner = client.runAsync(); @@ -1356,6 +1461,9 @@ private void asyncRunner_withReadFunction( @Test public void asyncRunnerReadRow() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); AsyncRunner runner = client.runAsync(); assertThrowsSessionNotFoundIfShouldFail( () -> @@ -1367,6 +1475,9 @@ public void asyncRunnerReadRow() throws InterruptedException { @Test public void asyncRunnerReadRowUsingIndex() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); AsyncRunner runner = client.runAsync(); assertThrowsSessionNotFoundIfShouldFail( () -> @@ -1380,6 +1491,9 @@ public void asyncRunnerReadRowUsingIndex() throws InterruptedException { @Test public void asyncRunnerUpdate() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); AsyncRunner runner = client.runAsync(); assertThrowsSessionNotFoundIfShouldFail( () -> get(runner.runAsync(txn -> txn.executeUpdateAsync(UPDATE_STATEMENT), executor))); @@ -1387,6 +1501,9 @@ public void asyncRunnerUpdate() throws InterruptedException { @Test public void asyncRunnerBatchUpdate() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); AsyncRunner runner = client.runAsync(); assertThrowsSessionNotFoundIfShouldFail( () -> @@ -1398,6 +1515,9 @@ public void asyncRunnerBatchUpdate() throws InterruptedException { @Test public void asyncRunnerBuffer() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); AsyncRunner runner = client.runAsync(); assertThrowsSessionNotFoundIfShouldFail( () -> @@ -1412,17 +1532,26 @@ public void asyncRunnerBuffer() throws InterruptedException { @Test public void asyncTransactionManagerAsyncSelect() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncTransactionManager_readAsync(input -> input.executeQueryAsync(SELECT1AND2)); } @Test public void asyncTransactionManagerAsyncRead() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncTransactionManager_readAsync( input -> input.readAsync("FOO", KeySet.all(), Collections.singletonList("BAR"))); } @Test public void asyncTransactionManagerAsyncReadUsingIndex() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncTransactionManager_readAsync( input -> input.readUsingIndexAsync( @@ -1431,6 +1560,9 @@ public void asyncTransactionManagerAsyncReadUsingIndex() throws InterruptedExcep private void asyncTransactionManager_readAsync( final Function fn) throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); final ExecutorService queryExecutor = Executors.newSingleThreadExecutor(); try (AsyncTransactionManager manager = client.transactionManagerAsync()) { TransactionContextFuture context = manager.beginAsync(); @@ -1475,17 +1607,26 @@ private void asyncTransactionManager_readAsync( @Test public void asyncTransactionManagerSelect() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncTransactionManager_readSync(input -> input.executeQuery(SELECT1AND2)); } @Test public void asyncTransactionManagerRead() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncTransactionManager_readSync( input -> input.read("FOO", KeySet.all(), Collections.singletonList("BAR"))); } @Test public void asyncTransactionManagerReadUsingIndex() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncTransactionManager_readSync( input -> input.readUsingIndex("FOO", "idx", KeySet.all(), Collections.singletonList("BAR"))); @@ -1493,6 +1634,9 @@ public void asyncTransactionManagerReadUsingIndex() throws InterruptedException private void asyncTransactionManager_readSync(final Function fn) throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); final ExecutorService queryExecutor = Executors.newSingleThreadExecutor(); try (AsyncTransactionManager manager = client.transactionManagerAsync()) { TransactionContextFuture context = manager.beginAsync(); @@ -1524,6 +1668,9 @@ private void asyncTransactionManager_readSync(final Function ApiFutures.immediateFuture( @@ -1532,6 +1679,9 @@ public void asyncTransactionManagerReadRow() throws InterruptedException { @Test public void asyncTransactionManagerReadRowUsingIndex() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncTransactionManager_readRowFunction( input -> ApiFutures.immediateFuture( @@ -1541,12 +1691,18 @@ public void asyncTransactionManagerReadRowUsingIndex() throws InterruptedExcepti @Test public void asyncTransactionManagerReadRowAsync() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncTransactionManager_readRowFunction( input -> input.readRowAsync("FOO", Key.of("foo"), Collections.singletonList("BAR"))); } @Test public void asyncTransactionManagerReadRowUsingIndexAsync() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncTransactionManager_readRowFunction( input -> input.readRowUsingIndexAsync( @@ -1555,6 +1711,9 @@ public void asyncTransactionManagerReadRowUsingIndexAsync() throws InterruptedEx private void asyncTransactionManager_readRowFunction( final Function> fn) throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); final ExecutorService queryExecutor = Executors.newSingleThreadExecutor(); try (AsyncTransactionManager manager = client.transactionManagerAsync()) { TransactionContextFuture context = manager.beginAsync(); @@ -1576,18 +1735,27 @@ private void asyncTransactionManager_readRowFunction( @Test public void asyncTransactionManagerUpdateAsync() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncTransactionManager_updateFunction( input -> input.executeUpdateAsync(UPDATE_STATEMENT), UPDATE_COUNT); } @Test public void asyncTransactionManagerUpdate() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncTransactionManager_updateFunction( input -> ApiFutures.immediateFuture(input.executeUpdate(UPDATE_STATEMENT)), UPDATE_COUNT); } @Test public void asyncTransactionManagerBatchUpdateAsync() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncTransactionManager_updateFunction( input -> input.batchUpdateAsync(Arrays.asList(UPDATE_STATEMENT, UPDATE_STATEMENT)), new long[] {UPDATE_COUNT, UPDATE_COUNT}); @@ -1595,6 +1763,9 @@ public void asyncTransactionManagerBatchUpdateAsync() throws InterruptedExceptio @Test public void asyncTransactionManagerBatchUpdate() throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); asyncTransactionManager_updateFunction( input -> ApiFutures.immediateFuture( @@ -1604,6 +1775,9 @@ public void asyncTransactionManagerBatchUpdate() throws InterruptedException { private void asyncTransactionManager_updateFunction( final Function> fn, T expected) throws InterruptedException { + assumeFalse( + "Multiplexed session do not throw a SessionNotFound errors. ", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); try (AsyncTransactionManager manager = client.transactionManagerAsync()) { TransactionContextFuture transaction = manager.beginAsync(); while (true) { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolLeakTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolLeakTest.java index 4672f03aeff..8ccea443dc1 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolLeakTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolLeakTest.java @@ -171,6 +171,9 @@ public void testIgnoreLeakedSession() { @Test public void testReadWriteTransactionExceptionOnCreateSession() { + assumeFalse( + "Session Leaks do not occur with Multiplexed Sessions", + isMultiplexedSessionsEnabledForRW()); readWriteTransactionTest( () -> mockSpanner.setBatchCreateSessionsExecutionTime( @@ -180,6 +183,9 @@ public void testReadWriteTransactionExceptionOnCreateSession() { @Test public void testReadWriteTransactionExceptionOnBegin() { + assumeFalse( + "Session Leaks do not occur with Multiplexed Sessions", + isMultiplexedSessionsEnabledForRW()); readWriteTransactionTest( () -> mockSpanner.setBeginTransactionExecutionTime( @@ -200,6 +206,9 @@ private void readWriteTransactionTest( @Test public void testTransactionManagerExceptionOnCreateSession() { + assumeFalse( + "Session Leaks do not occur with Multiplexed Sessions", + isMultiplexedSessionsEnabledForRW()); transactionManagerTest( () -> mockSpanner.setBatchCreateSessionsExecutionTime( @@ -209,6 +218,9 @@ public void testTransactionManagerExceptionOnCreateSession() { @Test public void testTransactionManagerExceptionOnBegin() { + assumeFalse( + "Session Leaks do not occur with Multiplexed Sessions", + isMultiplexedSessionsEnabledForRW()); assertThat(pool.getNumberOfSessionsInPool(), is(equalTo(0))); mockSpanner.setBeginTransactionExecutionTime( SimulatedExecutionTime.ofException(FAILED_PRECONDITION)); @@ -229,4 +241,11 @@ private void transactionManagerTest(Runnable setup, int expectedNumberOfSessions } assertEquals(expectedNumberOfSessionsAfterExecution, pool.getNumberOfSessionsInPool()); } + + private boolean isMultiplexedSessionsEnabledForRW() { + if (spanner.getOptions() == null || spanner.getOptions().getSessionPoolOptions() == null) { + return false; + } + return spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW(); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionChannelHintTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionChannelHintTest.java index bd346c4f18b..14658210f44 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionChannelHintTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionChannelHintTest.java @@ -188,6 +188,8 @@ private SpannerOptions createSpannerOptions() { .setCompressorName("gzip") .setHost("http://" + endpoint) .setCredentials(NoCredentials.getInstance()) + .setSessionPoolOption( + SessionPoolOptions.newBuilder().setSkipVerifyingBeginTransactionForMuxRW(true).build()) .build(); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java index 10b13125152..aee3d5ed5b4 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java @@ -234,6 +234,21 @@ public void usesPreparedTransaction() { com.google.protobuf.Timestamp.newBuilder() .setSeconds(System.currentTimeMillis() * 1000)) .build())); + when(rpc.createSession( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyMap(), + Mockito.eq(null), + Mockito.eq(true))) + .thenAnswer( + invocation -> + Session.newBuilder() + .setName(invocation.getArguments()[0] + "/sessions/1") + .setMultiplexed(true) + .setCreateTime( + com.google.protobuf.Timestamp.newBuilder() + .setSeconds(System.currentTimeMillis() * 1000)) + .build()); when(rpc.beginTransactionAsync( Mockito.any(BeginTransactionRequest.class), Mockito.anyMap(), eq(true))) .thenAnswer( @@ -300,6 +315,21 @@ public void inlineBegin() { com.google.protobuf.Timestamp.newBuilder() .setSeconds(System.currentTimeMillis() * 1000)) .build())); + when(rpc.createSession( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyMap(), + Mockito.eq(null), + Mockito.eq(true))) + .thenAnswer( + invocation -> + Session.newBuilder() + .setName(invocation.getArguments()[0] + "/sessions/1") + .setMultiplexed(true) + .setCreateTime( + com.google.protobuf.Timestamp.newBuilder() + .setSeconds(System.currentTimeMillis() * 1000)) + .build()); when(rpc.beginTransactionAsync( Mockito.any(BeginTransactionRequest.class), Mockito.anyMap(), eq(true))) .thenAnswer( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java index 1fd6817ea96..d8bd6ed448d 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java @@ -186,6 +186,21 @@ public void usesPreparedTransaction() { .setCreateTime( Timestamp.newBuilder().setSeconds(System.currentTimeMillis() * 1000)) .build())); + when(rpc.createSession( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyMap(), + Mockito.eq(null), + Mockito.eq(true))) + .thenAnswer( + invocation -> + Session.newBuilder() + .setName(invocation.getArguments()[0] + "/sessions/1") + .setMultiplexed(true) + .setCreateTime( + com.google.protobuf.Timestamp.newBuilder() + .setSeconds(System.currentTimeMillis() * 1000)) + .build()); when(rpc.beginTransactionAsync( Mockito.any(BeginTransactionRequest.class), Mockito.anyMap(), eq(true))) .thenAnswer( From 42ff2e520b00ff7473020132d2f31f338604d928 Mon Sep 17 00:00:00 2001 From: Sri Harsha CH Date: Thu, 2 Jan 2025 10:42:20 +0000 Subject: [PATCH 2/3] chore(Spanner): fix tests --- .../java/com/google/cloud/spanner/DatabaseClientImplTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java index acba7b2b891..ed41822c150 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java @@ -3100,7 +3100,7 @@ public void testDatabaseOrInstanceDoesNotExistOnCreate() { // 1. CreateSession for the multiplexed session. // There will be no BatchCreateSessions request in case of multiplexed sessions, because // the session pool options has min size of 0. - assertThat(mockSpanner.getRequests()).hasSize(1); + assertThat(mockSpanner.getRequests()).hasSize(2); } } mockSpanner.reset(); From 502270c15cd95b07a00175df5b7c83ec23066945 Mon Sep 17 00:00:00 2001 From: Sri Harsha CH Date: Thu, 2 Jan 2025 10:49:09 +0000 Subject: [PATCH 3/3] chore(spanner): fix tests --- .../cloud/spanner/DatabaseClientImplTest.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java index ed41822c150..1ee60509d55 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java @@ -3093,14 +3093,15 @@ public void testDatabaseOrInstanceDoesNotExistOnCreate() { .readWriteTransaction() .run(transaction -> transaction.executeUpdate(UPDATE_STATEMENT))); // No additional requests should have been sent by the client. + // Note that in case of the use of multiplexed sessions, then we have 2 requests: // Note that in case of the use of regular sessions, then we have 1 request: // 1. BatchCreateSessions for the session pool. - // Note that in case of the use of multiplexed sessions for read-only and read-write, - // then we have 1 request: - // 1. CreateSession for the multiplexed session. - // There will be no BatchCreateSessions request in case of multiplexed sessions, because - // the session pool options has min size of 0. - assertThat(mockSpanner.getRequests()).hasSize(2); + // 2. CreateSession for the multiplexed session. + assertThat(mockSpanner.getRequests()) + .hasSize( + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSession() + ? 2 + : 1); } } mockSpanner.reset();