|
16 | 16 |
|
17 | 17 | package com.google.cloud.spanner; |
18 | 18 |
|
| 19 | +import static com.google.cloud.spanner.MockSpannerTestUtil.INVALID_UPDATE_STATEMENT; |
| 20 | +import static com.google.cloud.spanner.MockSpannerTestUtil.UPDATE_COUNT; |
| 21 | +import static com.google.cloud.spanner.MockSpannerTestUtil.UPDATE_STATEMENT; |
19 | 22 | import static com.google.common.truth.Truth.assertThat; |
20 | 23 | import static org.junit.Assert.assertEquals; |
21 | 24 | import static org.junit.Assert.assertFalse; |
@@ -58,6 +61,12 @@ public class MultiplexedSessionDatabaseClientMockServerTest extends AbstractMock |
58 | 61 | public static void setupResults() { |
59 | 62 | mockSpanner.putStatementResults( |
60 | 63 | StatementResult.query(STATEMENT, new RandomResultSetGenerator(1).generate())); |
| 64 | + |
| 65 | + mockSpanner.putStatementResult(StatementResult.update(UPDATE_STATEMENT, UPDATE_COUNT)); |
| 66 | + mockSpanner.putStatementResult( |
| 67 | + StatementResult.exception( |
| 68 | + INVALID_UPDATE_STATEMENT, |
| 69 | + Status.INVALID_ARGUMENT.withDescription("invalid statement").asRuntimeException())); |
61 | 70 | } |
62 | 71 |
|
63 | 72 | @Before |
@@ -467,6 +476,106 @@ public void testWriteAtLeastOnceWithExcludeTxnFromChangeStreams() { |
467 | 476 | assertEquals(1L, client.multiplexedSessionDatabaseClient.getNumSessionsReleased().get()); |
468 | 477 | } |
469 | 478 |
|
| 479 | + // TODO(sriharshach): Uncomment test once Lock order preservation proto is published |
| 480 | + /* |
| 481 | + @Test |
| 482 | + public void testAbortedReadWriteTxnUsesPreviousTxnIdOnRetryWithInlineBegin() throws InterruptedException { |
| 483 | + DatabaseClientImpl client = |
| 484 | + (DatabaseClientImpl) spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); |
| 485 | + // Force the Commit RPC to return Aborted the first time it is called. The exception is cleared |
| 486 | + // after the first call, so the retry should succeed. |
| 487 | + mockSpanner.setCommitExecutionTime( |
| 488 | + SimulatedExecutionTime.ofException( |
| 489 | + mockSpanner.createAbortedException(ByteString.copyFromUtf8("test")))); |
| 490 | + Thread.sleep(10000); |
| 491 | + TransactionRunner runner = client.readWriteTransaction(); |
| 492 | + runner.run( |
| 493 | + transaction -> { |
| 494 | + try (ResultSet resultSet = |
| 495 | + transaction.executeQuery(STATEMENT)) { |
| 496 | + while (resultSet.next()) {} |
| 497 | + } |
| 498 | + return null; |
| 499 | + }); |
| 500 | +
|
| 501 | + List<ExecuteSqlRequest> executeSqlRequests = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class); |
| 502 | + assertEquals(2, executeSqlRequests.size()); |
| 503 | +
|
| 504 | + // Verify the requests are executed using multiplexed sessions |
| 505 | + for (ExecuteSqlRequest request : executeSqlRequests) { |
| 506 | + assertTrue(mockSpanner.getSession(request.getSession()).getMultiplexed()); |
| 507 | + } |
| 508 | +
|
| 509 | + // Verify that the first request uses inline begin, and the previous transaction ID is set to ByteString.EMPTY |
| 510 | + assertTrue(executeSqlRequests.get(0).hasTransaction()); |
| 511 | + assertTrue(executeSqlRequests.get(0).getTransaction().hasBegin()); |
| 512 | + assertTrue(executeSqlRequests.get(0).getTransaction().getBegin().hasReadWrite()); |
| 513 | + assertNotNull(executeSqlRequests.get(0).getTransaction().getBegin().getReadWrite() |
| 514 | + .getMultiplexedSessionPreviousTransactionId()); |
| 515 | + assertThat(executeSqlRequests.get(0).getTransaction().getBegin().getReadWrite().getMultiplexedSessionPreviousTransactionId()).isEqualTo(ByteString.EMPTY); |
| 516 | +
|
| 517 | + // Verify that the second request uses inline begin, and the previous transaction ID is set appropriately |
| 518 | + assertTrue(executeSqlRequests.get(1).hasTransaction()); |
| 519 | + assertTrue(executeSqlRequests.get(1).getTransaction().hasBegin()); |
| 520 | + assertTrue(executeSqlRequests.get(1).getTransaction().getBegin().hasReadWrite()); |
| 521 | + assertNotNull(executeSqlRequests.get(1).getTransaction().getBegin().getReadWrite() |
| 522 | + .getMultiplexedSessionPreviousTransactionId()); |
| 523 | + assertThat(executeSqlRequests.get(1).getTransaction().getBegin().getReadWrite().getMultiplexedSessionPreviousTransactionId()).isNotEqualTo(ByteString.EMPTY); |
| 524 | + } |
| 525 | + */ |
| 526 | + |
| 527 | + // TODO(sriharshach): Uncomment test once Lock order preservation proto is published |
| 528 | + /* |
| 529 | + @Test |
| 530 | + public void testAbortedReadWriteTxnUsesPreviousTxnIdOnRetryWithExplicitBegin() throws InterruptedException { |
| 531 | + DatabaseClientImpl client = |
| 532 | + (DatabaseClientImpl) spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); |
| 533 | + // Force the Commit RPC to return Aborted the first time it is called. The exception is cleared |
| 534 | + // after the first call, so the retry should succeed. |
| 535 | + mockSpanner.setCommitExecutionTime( |
| 536 | + SimulatedExecutionTime.ofException( |
| 537 | + mockSpanner.createAbortedException(ByteString.copyFromUtf8("test")))); |
| 538 | + Thread.sleep(10000); |
| 539 | + TransactionRunner runner = client.readWriteTransaction(); |
| 540 | + Long updateCount = runner.run( |
| 541 | + transaction -> { |
| 542 | + // This update statement carries the BeginTransaction, but fails. This will |
| 543 | + // cause the entire transaction to be retried with an explicit |
| 544 | + // BeginTransaction RPC to ensure all statements in the transaction are |
| 545 | + // actually executed against the same transaction. |
| 546 | + SpannerException e = |
| 547 | + assertThrows( |
| 548 | + SpannerException.class, |
| 549 | + () -> transaction.executeUpdate(INVALID_UPDATE_STATEMENT)); |
| 550 | + assertEquals(ErrorCode.INVALID_ARGUMENT, e.getErrorCode()); |
| 551 | + return transaction.executeUpdate(UPDATE_STATEMENT); |
| 552 | + }); |
| 553 | +
|
| 554 | + assertThat(updateCount).isEqualTo(1L); |
| 555 | + List<BeginTransactionRequest> beginTransactionRequests = mockSpanner.getRequestsOfType(BeginTransactionRequest.class); |
| 556 | + assertEquals(2, beginTransactionRequests.size()); |
| 557 | +
|
| 558 | + // Verify the requests are executed using multiplexed sessions |
| 559 | + for (BeginTransactionRequest request : beginTransactionRequests) { |
| 560 | + assertTrue(mockSpanner.getSession(request.getSession()).getMultiplexed()); |
| 561 | + } |
| 562 | +
|
| 563 | + // Verify that explicit begin transaction is called during retry, and the previous transaction ID is set to ByteString.EMPTY |
| 564 | + assertTrue(beginTransactionRequests.get(0).hasOptions()); |
| 565 | + assertTrue(beginTransactionRequests.get(0).getOptions().hasReadWrite()); |
| 566 | + assertNotNull(beginTransactionRequests.get(0).getOptions().getReadWrite() |
| 567 | + .getMultiplexedSessionPreviousTransactionId()); |
| 568 | + assertThat(beginTransactionRequests.get(0).getOptions().getReadWrite().getMultiplexedSessionPreviousTransactionId()).isEqualTo(ByteString.EMPTY); |
| 569 | +
|
| 570 | + // The previous transaction with id (txn1) fails during commit operation with ABORTED error. |
| 571 | + // Verify that explicit begin transaction is called during retry, and the previous transaction ID is not ByteString.EMPTY (should be set to txn1) |
| 572 | + assertTrue(beginTransactionRequests.get(1).hasOptions()); |
| 573 | + assertTrue(beginTransactionRequests.get(1).getOptions().hasReadWrite()); |
| 574 | + assertNotNull(beginTransactionRequests.get(1).getOptions().getReadWrite().getMultiplexedSessionPreviousTransactionId()); |
| 575 | + assertThat(beginTransactionRequests.get(1).getOptions().getReadWrite().getMultiplexedSessionPreviousTransactionId()).isNotEqualTo(ByteString.EMPTY); |
| 576 | + } |
| 577 | + */ |
| 578 | + |
470 | 579 | private void waitForSessionToBeReplaced(DatabaseClientImpl client) { |
471 | 580 | assertNotNull(client.multiplexedSessionDatabaseClient); |
472 | 581 | SessionReference sessionReference = |
|
0 commit comments