Skip to content

Commit abc7fc4

Browse files
committed
chore(spanner): verify error message of the exeception
1 parent 8cafd99 commit abc7fc4

File tree

3 files changed

+78
-7
lines changed

3 files changed

+78
-7
lines changed

google-cloud-spanner/src/main/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClient.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ void onError(SpannerException spannerException) {
9898
this.client.resourceNotFoundException.set((ResourceNotFoundException) spannerException);
9999
}
100100
// Mark multiplexed sessions for RW as unimplemented and fall back to regular sessions if
101-
// UNIMPLEMENTED is returned.
101+
// UNIMPLEMENTED with error message "Transaction type read_write not supported with
102+
// multiplexed sessions" is returned.
102103
this.client.maybeMarkUnimplementedForRW(spannerException);
103104
}
104105

@@ -172,7 +173,6 @@ public void close() {
172173
/** The current multiplexed session that is used by this client. */
173174
private final AtomicReference<ApiFuture<SessionReference>> multiplexedSessionReference;
174175

175-
@VisibleForTesting
176176
/**
177177
* The Transaction response returned by the BeginTransaction request with read-write when a
178178
* multiplexed session is created during client initialization.
@@ -256,7 +256,7 @@ public void onSessionReady(SessionImpl session) {
256256
.getOptions()
257257
.getSessionPoolOptions()
258258
.getUseMultiplexedSessionForRW()) {
259-
verifyBeginTransactionWithRWOnMultiplexedSession(session.getName());
259+
verifyBeginTransactionWithRWOnMultiplexedSessionAsync(session.getName());
260260
}
261261
}
262262

@@ -301,14 +301,25 @@ private void maybeMarkUnimplemented(Throwable t) {
301301

302302
private void maybeMarkUnimplementedForRW(SpannerException spannerException) {
303303
if (spannerException.getErrorCode() == ErrorCode.UNIMPLEMENTED
304-
// && spannerException.getCause().getMessage().contains("Transaction type read_write not
305-
// supported with multiplexed sessions")
306-
) {
304+
&& verifyErrorMessage(
305+
spannerException,
306+
"Transaction type read_write not supported with multiplexed sessions")) {
307307
unimplementedForRW.set(true);
308308
}
309309
}
310310

311-
private void verifyBeginTransactionWithRWOnMultiplexedSession(String sessionName) {
311+
private boolean verifyErrorMessage(SpannerException spannerException, String message) {
312+
if (spannerException.getCause() == null) {
313+
return false;
314+
}
315+
if (spannerException.getCause().getMessage() == null) {
316+
return false;
317+
}
318+
return spannerException.getCause().getMessage().contains(message);
319+
}
320+
321+
private void verifyBeginTransactionWithRWOnMultiplexedSessionAsync(String sessionName) {
322+
// TODO: Remove once this is guaranteed to be available.
312323
// annotate the explict BeginTransactionRequest with a transaction tag
313324
// "multiplexed-rw-background-begin-txn" to avoid storing this request on mock spanner.
314325
// this is to safeguard other mock spanner tests whose BeginTransaction request count will

google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,6 +1872,7 @@ private Transaction getTemporaryTransactionOrNull(TransactionSelector tx) {
18721872
@Override
18731873
public void beginTransaction(
18741874
BeginTransactionRequest request, StreamObserver<Transaction> responseObserver) {
1875+
// TODO: Remove once this is guaranteed to be available.
18751876
// Skip storing the explicit BeginTransactionRequest used to verify read-write transaction
18761877
// server availability on multiplexed sessions.
18771878
// This code will be removed once read-write multiplexed sessions are stable on the backend,

google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientMockServerTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,65 @@ public void testReadWriteUnimplemented_firstReceivesError_secondFallsBackToRegul
15161516
assertEquals(1L, client.multiplexedSessionDatabaseClient.getNumSessionsReleased().get());
15171517
}
15181518

1519+
@Test
1520+
public void testOtherUnimplementedError_ReadWriteTransactionStillUsesMultiplexedSession() {
1521+
mockSpanner.setExecuteStreamingSqlExecutionTime(
1522+
SimulatedExecutionTime.ofException(
1523+
Status.UNIMPLEMENTED
1524+
.withDescription("Multiplexed sessions are not supported.")
1525+
.asRuntimeException()));
1526+
1527+
DatabaseClientImpl client =
1528+
(DatabaseClientImpl) spanner.getDatabaseClient(DatabaseId.of("p", "i", "d"));
1529+
1530+
// Wait until the initial BeginTransaction RPC with read-write is complete.
1531+
assertNotNull(client.multiplexedSessionDatabaseClient);
1532+
Transaction txn =
1533+
client.multiplexedSessionDatabaseClient.getReadWriteBeginTransactionReference();
1534+
assertNotNull(txn);
1535+
assertNotNull(txn.getId());
1536+
assertFalse(client.multiplexedSessionDatabaseClient.unimplementedForRW.get());
1537+
1538+
// Try to execute a query using single use transaction.
1539+
try (ResultSet resultSet = client.singleUse().executeQuery(STATEMENT)) {
1540+
SpannerException spannerException = assertThrows(SpannerException.class, resultSet::next);
1541+
assertEquals(ErrorCode.UNIMPLEMENTED, spannerException.getErrorCode());
1542+
}
1543+
// Verify other UNIMPLEMENTED errors does not turn off read-write transactions to use
1544+
// multiplexed sessions.
1545+
assertFalse(client.multiplexedSessionDatabaseClient.unimplementedForRW.get());
1546+
1547+
// The read-write transaction should use multiplexed sessions and succeed.
1548+
client
1549+
.readWriteTransaction()
1550+
.run(
1551+
transaction -> {
1552+
// Returns a ResultSet containing the precommit token (ResultSetPrecommitToken)
1553+
transaction.executeUpdate(UPDATE_STATEMENT);
1554+
1555+
// Verify that a precommit token is received. This guarantees that the read-write
1556+
// transaction was executed on a multiplexed session.
1557+
TransactionContextImpl impl = (TransactionContextImpl) transaction;
1558+
assertNotNull(impl.getLatestPrecommitToken());
1559+
assertEquals(
1560+
ByteString.copyFromUtf8("ResultSetPrecommitToken"),
1561+
impl.getLatestPrecommitToken().getPrecommitToken());
1562+
return null;
1563+
});
1564+
1565+
// Verify that two ExecuteSqlRequests were received and second one uses a multiplexed session.
1566+
assertEquals(2, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
1567+
List<ExecuteSqlRequest> requests = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class);
1568+
1569+
Session session2 = mockSpanner.getSession(requests.get(1).getSession());
1570+
assertNotNull(session2);
1571+
assertTrue(session2.getMultiplexed());
1572+
1573+
assertNotNull(client.multiplexedSessionDatabaseClient);
1574+
assertEquals(2L, client.multiplexedSessionDatabaseClient.getNumSessionsAcquired().get());
1575+
assertEquals(2L, client.multiplexedSessionDatabaseClient.getNumSessionsReleased().get());
1576+
}
1577+
15191578
private void waitForSessionToBeReplaced(DatabaseClientImpl client) {
15201579
assertNotNull(client.multiplexedSessionDatabaseClient);
15211580
SessionReference sessionReference =

0 commit comments

Comments
 (0)