Search in sources :

Example 6 with AbortedDueToConcurrentModificationException

use of com.google.cloud.spanner.AbortedDueToConcurrentModificationException in project java-spanner by googleapis.

the class ITTransactionRetryTest method testAbortWithExceptionOnSelectAndConcurrentModification.

/**
 * Test that shows the following:
 *
 * <ol>
 *   <li>Insert two records into table TEST and commit.
 *   <li>Try to query the non-existing table FOO. This will lead to an exception.
 *   <li>Query all the records from the TEST table and consume the result set.
 *   <li>Open another connection and create the table FOO.
 *   <li>Insert another record into TEST that aborts.
 *   <li>The transaction is internally retried. The retry fails as the SELECT statement on FOO
 *       will now succeed.
 * </ol>
 */
@Test
public void testAbortWithExceptionOnSelectAndConcurrentModification() {
    assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator());
    boolean abortedDueToConcurrentModification = false;
    AbortInterceptor interceptor = new AbortInterceptor(0);
    // first insert two test records
    try (ITConnection connection = createConnection()) {
        connection.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test 1')"));
        connection.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (2, 'test 2')"));
        connection.commit();
    }
    try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) {
        // do a select that will fail
        boolean expectedException = false;
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM FOO"))) {
            while (rs.next()) {
            // do nothing
            }
        } catch (SpannerException e) {
            // expected
            expectedException = true;
        }
        assertThat(expectedException, is(true));
        // do a select that will succeed
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM TEST"))) {
            while (rs.next()) {
            // do nothing
            }
        }
        // CREATE FOO
        try (ITConnection connection2 = createConnection()) {
            connection2.setAutocommit(true);
            connection2.execute(Statement.of("CREATE TABLE FOO (ID INT64, NAME STRING(100)) PRIMARY KEY (ID)"));
        }
        // Now try to do an insert that will abort. The subsequent retry will fail as the SELECT *
        // FROM FOO now returns a result.
        interceptor.setProbability(1.0);
        interceptor.setOnlyInjectOnce(true);
        try {
            connection.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (3, 'test 3')"));
        } catch (AbortedDueToConcurrentModificationException e) {
            abortedDueToConcurrentModification = true;
        }
    }
    // DROP FOO regardless of the result to avoid any interference with other test cases
    try (ITConnection connection2 = createConnection()) {
        connection2.setAutocommit(true);
        connection2.execute(Statement.of("DROP TABLE FOO"));
    }
    assertThat(abortedDueToConcurrentModification, is(true));
    assertRetryStatistics(1, 1, 0);
}
Also used : ResultSet(com.google.cloud.spanner.ResultSet) SpannerException(com.google.cloud.spanner.SpannerException) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) ParallelIntegrationTest(com.google.cloud.spanner.ParallelIntegrationTest) Test(org.junit.Test) ITAbstractSpannerTest(com.google.cloud.spanner.connection.ITAbstractSpannerTest)

Example 7 with AbortedDueToConcurrentModificationException

use of com.google.cloud.spanner.AbortedDueToConcurrentModificationException in project java-spanner by googleapis.

the class ITTransactionRetryTest method testAbortWithExceptionOnInsertAndConcurrentModification.

/**
 * Test that shows the following:
 *
 * <ol>
 *   <li>Insert two records into table TEST and commit.
 *   <li>Try to insert a record in the non-existing table FOO. This will lead to an exception.
 *   <li>Query all the records from the TEST table and consume the result set.
 *   <li>Open another connection and create the table FOO.
 *   <li>Insert another record into TEST that aborts.
 *   <li>The transaction is internally retried. The retry fails as the insert statement on FOO
 *       will now succeed.
 * </ol>
 */
@Test
public void testAbortWithExceptionOnInsertAndConcurrentModification() {
    assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator());
    boolean abortedDueToConcurrentModification = false;
    AbortInterceptor interceptor = new AbortInterceptor(0);
    // first insert two test records
    try (ITConnection connection = createConnection()) {
        connection.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test 1')"));
        connection.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (2, 'test 2')"));
        connection.commit();
    }
    try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) {
        // do an insert that will fail
        boolean expectedException = false;
        try {
            connection.executeUpdate(Statement.of("INSERT INTO FOO (ID, NAME) VALUES (1, 'test 1')"));
        } catch (SpannerException e) {
            // expected
            expectedException = true;
        }
        assertThat(expectedException, is(true));
        // do a select that will succeed
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM TEST"))) {
            while (rs.next()) {
            // do nothing
            }
        }
        // CREATE FOO
        try (ITConnection connection2 = createConnection()) {
            connection2.setAutocommit(true);
            connection2.execute(Statement.of("CREATE TABLE FOO (ID INT64, NAME STRING(100)) PRIMARY KEY (ID)"));
        }
        // Now try to do an insert that will abort. The subsequent retry will fail as the INSERT INTO
        // FOO now succeeds.
        interceptor.setProbability(1.0);
        interceptor.setOnlyInjectOnce(true);
        try {
            connection.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (3, 'test 3')"));
        } catch (AbortedDueToConcurrentModificationException e) {
            abortedDueToConcurrentModification = true;
        }
    }
    // DROP FOO regardless of the result to avoid any interference with other test cases
    try (ITConnection connection2 = createConnection()) {
        connection2.setAutocommit(true);
        connection2.execute(Statement.of("DROP TABLE FOO"));
    }
    assertThat(abortedDueToConcurrentModification, is(true));
    assertRetryStatistics(1, 1, 0);
}
Also used : ResultSet(com.google.cloud.spanner.ResultSet) SpannerException(com.google.cloud.spanner.SpannerException) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) ParallelIntegrationTest(com.google.cloud.spanner.ParallelIntegrationTest) Test(org.junit.Test) ITAbstractSpannerTest(com.google.cloud.spanner.connection.ITAbstractSpannerTest)

Example 8 with AbortedDueToConcurrentModificationException

use of com.google.cloud.spanner.AbortedDueToConcurrentModificationException in project java-spanner by googleapis.

the class ConnectionAsyncApiAbortedTest method testUpdateAndQueryAbortedMidway_UpdateCountChanged.

@Test
public void testUpdateAndQueryAbortedMidway_UpdateCountChanged() throws InterruptedException {
    mockSpanner.setExecuteStreamingSqlExecutionTime(SimulatedExecutionTime.ofStreamException(mockSpanner.createAbortedException(ByteString.copyFromUtf8("test")), RANDOM_RESULT_SET_ROW_COUNT / 2));
    final RetryCounter counter = new RetryCounter();
    try (Connection connection = createConnection(counter)) {
        assertThat(counter.retryCount).isEqualTo(0);
        final CountDownLatch updateLatch = new CountDownLatch(1);
        final CountDownLatch queryLatch = new CountDownLatch(1);
        ApiFuture<Void> finished;
        try (AsyncResultSet rs = connection.executeQueryAsync(SELECT_RANDOM_STATEMENT, Options.bufferRows(RANDOM_RESULT_SET_ROW_COUNT / 2 - 1))) {
            finished = rs.setCallback(singleThreadedExecutor, resultSet -> {
                // Indicate that the query has been executed.
                queryLatch.countDown();
                try {
                    // Wait until the update is on its way.
                    updateLatch.await(10L, TimeUnit.SECONDS);
                    while (true) {
                        switch(resultSet.tryNext()) {
                            case OK:
                                break;
                            case DONE:
                                return CallbackResponse.DONE;
                            case NOT_READY:
                                return CallbackResponse.CONTINUE;
                        }
                    }
                } catch (InterruptedException e) {
                    throw SpannerExceptionFactory.propagateInterrupt(e);
                }
            });
        }
        // Wait until the query has actually executed.
        queryLatch.await(10L, TimeUnit.SECONDS);
        // Execute an update statement and wait until it has finished before allowing the
        // AsyncResultSet to continue processing. Also change the result of the update statement after
        // it has finished. The AsyncResultSet will see an aborted transaction halfway, and then
        // during the retry, it will get a different result for this update statement. That will cause
        // the retry to be aborted.
        get(connection.executeUpdateAsync(INSERT_STATEMENT));
        try {
            mockSpanner.putStatementResult(StatementResult.update(INSERT_STATEMENT, UPDATE_COUNT + 1));
            updateLatch.countDown();
            get(finished);
            fail("Missing expected exception");
        } catch (AbortedDueToConcurrentModificationException e) {
            assertThat(counter.retryCount).isEqualTo(1);
        } finally {
            mockSpanner.putStatementResult(StatementResult.update(INSERT_STATEMENT, UPDATE_COUNT));
        }
        // Verify the order of the statements on the server.
        List<? extends AbstractMessage> requests = Lists.newArrayList(Collections2.filter(mockSpanner.getRequests(), input -> input instanceof ExecuteSqlRequest));
        // The entire transaction should be retried, but will not succeed as the result of the update
        // statement was different during the retry.
        assertThat(requests).hasSize(4);
        assertThat(((ExecuteSqlRequest) requests.get(0)).getSeqno()).isEqualTo(1L);
        assertThat(((ExecuteSqlRequest) requests.get(0)).getSql()).isEqualTo(SELECT_RANDOM_STATEMENT.getSql());
        assertThat(((ExecuteSqlRequest) requests.get(1)).getSeqno()).isEqualTo(2L);
        assertThat(((ExecuteSqlRequest) requests.get(1)).getSql()).isEqualTo(INSERT_STATEMENT.getSql());
        assertThat(((ExecuteSqlRequest) requests.get(2)).getSeqno()).isEqualTo(1L);
        assertThat(((ExecuteSqlRequest) requests.get(2)).getSql()).isEqualTo(SELECT_RANDOM_STATEMENT.getSql());
        assertThat(((ExecuteSqlRequest) requests.get(3)).getSeqno()).isEqualTo(2L);
        assertThat(((ExecuteSqlRequest) requests.get(3)).getSql()).isEqualTo(INSERT_STATEMENT.getSql());
    }
}
Also used : MoreExecutors(com.google.common.util.concurrent.MoreExecutors) BeforeClass(org.junit.BeforeClass) SpannerExceptionFactory(com.google.cloud.spanner.SpannerExceptionFactory) Timestamp(com.google.cloud.Timestamp) Collections2(com.google.common.collect.Collections2) StatementResult(com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult) ArrayList(java.util.ArrayList) Lists(com.google.common.collect.Lists) ImmutableList(com.google.common.collect.ImmutableList) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) After(org.junit.After) Assert.fail(org.junit.Assert.fail) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) SimulatedExecutionTime(com.google.cloud.spanner.MockSpannerServiceImpl.SimulatedExecutionTime) ExecutorService(java.util.concurrent.ExecutorService) Before(org.junit.Before) AsyncResultSet(com.google.cloud.spanner.AsyncResultSet) AfterClass(org.junit.AfterClass) CallbackResponse(com.google.cloud.spanner.AsyncResultSet.CallbackResponse) AbstractMessage(com.google.protobuf.AbstractMessage) Executor(java.util.concurrent.Executor) SpannerApiFutures.get(com.google.cloud.spanner.SpannerApiFutures.get) ITConnection(com.google.cloud.spanner.connection.ITAbstractSpannerTest.ITConnection) Test(org.junit.Test) ReadyCallback(com.google.cloud.spanner.AsyncResultSet.ReadyCallback) Truth.assertThat(com.google.common.truth.Truth.assertThat) Options(com.google.cloud.spanner.Options) Executors(java.util.concurrent.Executors) ApiFuture(com.google.api.core.ApiFuture) SettableApiFuture(com.google.api.core.SettableApiFuture) Statement(com.google.cloud.spanner.Statement) ByteString(com.google.protobuf.ByteString) TimeUnit(java.util.concurrent.TimeUnit) CountDownLatch(java.util.concurrent.CountDownLatch) List(java.util.List) ExecuteSqlRequest(com.google.spanner.v1.ExecuteSqlRequest) ExecuteSqlRequest(com.google.spanner.v1.ExecuteSqlRequest) AsyncResultSet(com.google.cloud.spanner.AsyncResultSet) ITConnection(com.google.cloud.spanner.connection.ITAbstractSpannerTest.ITConnection) CountDownLatch(java.util.concurrent.CountDownLatch) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) Test(org.junit.Test)

Example 9 with AbortedDueToConcurrentModificationException

use of com.google.cloud.spanner.AbortedDueToConcurrentModificationException in project java-spanner by googleapis.

the class ConnectionAsyncApiAbortedTest method testBlindUpdateAborted_SelectResults.

@Test
public void testBlindUpdateAborted_SelectResults() {
    final Statement update1 = Statement.of("UPDATE FOO SET BAR=1 WHERE BAZ=100");
    mockSpanner.putStatementResult(StatementResult.update(update1, 100));
    RetryCounter counter = new RetryCounter();
    try (Connection connection = createConnection(counter)) {
        // Execute an update statement and then change the result for the next time it is executed.
        connection.executeUpdate(update1);
        // Abort on the next statement. The retry should now fail because of the changed result of the
        // first update.
        mockSpanner.abortNextStatement();
        mockSpanner.putStatementResult(StatementResult.update(update1, 200));
        connection.executeUpdateAsync(INSERT_STATEMENT);
        ApiFuture<Void> commit = connection.commitAsync();
        try (AsyncResultSet rs = connection.executeQueryAsync(SELECT_RANDOM_STATEMENT)) {
            while (rs.next()) {
            }
        }
        get(connection.commitAsync());
        try {
            get(commit);
            fail("Missing expected exception");
        } catch (AbortedDueToConcurrentModificationException e) {
            assertThat(counter.retryCount).isEqualTo(1);
        }
    }
}
Also used : AsyncResultSet(com.google.cloud.spanner.AsyncResultSet) Statement(com.google.cloud.spanner.Statement) ITConnection(com.google.cloud.spanner.connection.ITAbstractSpannerTest.ITConnection) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) Test(org.junit.Test)

Example 10 with AbortedDueToConcurrentModificationException

use of com.google.cloud.spanner.AbortedDueToConcurrentModificationException in project java-spanner by googleapis.

the class ConnectionAsyncApiAbortedTest method testBlindUpdateAborted_ThenAsyncQuery_WithConcurrentModification.

@Test
public void testBlindUpdateAborted_ThenAsyncQuery_WithConcurrentModification() {
    Statement update1 = Statement.of("UPDATE FOO SET BAR=1 WHERE BAZ=100");
    mockSpanner.putStatementResult(StatementResult.update(update1, 100));
    RetryCounter counter = new RetryCounter();
    try (Connection connection = createConnection(counter)) {
        // Execute an update statement and then change the result for the next time it is executed.
        get(connection.executeUpdateAsync(update1));
        mockSpanner.putStatementResult(StatementResult.update(update1, 200));
        // Abort on the next statement. The retry should now fail because of the changed result of the
        // first update.
        mockSpanner.abortNextStatement();
        connection.executeUpdateAsync(INSERT_STATEMENT);
        // AbortedDueToConcurrentModificationException.
        try (AsyncResultSet rs = connection.executeQueryAsync(SELECT_RANDOM_STATEMENT)) {
            ApiFuture<Void> fut = rs.setCallback(singleThreadedExecutor, resultSet -> {
                // The following line should throw AbortedDueToConcurrentModificationException.
                resultSet.tryNext();
                return CallbackResponse.DONE;
            });
            try {
                assertThat(get(fut)).isNull();
                fail("Missing expected exception");
            } catch (AbortedDueToConcurrentModificationException e) {
                assertThat(counter.retryCount).isEqualTo(1);
            }
        }
        // Ensure that a rollback and then a new statement does succeed.
        connection.rollbackAsync();
        try (AsyncResultSet rs = connection.executeQueryAsync(SELECT_RANDOM_STATEMENT)) {
            ApiFuture<Void> fut = rs.setCallback(singleThreadedExecutor, resultSet -> {
                resultSet.tryNext();
                return CallbackResponse.DONE;
            });
            assertThat(get(fut)).isNull();
        }
        get(connection.commitAsync());
    }
}
Also used : AsyncResultSet(com.google.cloud.spanner.AsyncResultSet) Statement(com.google.cloud.spanner.Statement) ITConnection(com.google.cloud.spanner.connection.ITAbstractSpannerTest.ITConnection) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) Test(org.junit.Test)

Aggregations

AbortedDueToConcurrentModificationException (com.google.cloud.spanner.AbortedDueToConcurrentModificationException)21 Test (org.junit.Test)21 ParallelIntegrationTest (com.google.cloud.spanner.ParallelIntegrationTest)15 ResultSet (com.google.cloud.spanner.ResultSet)15 ITAbstractSpannerTest (com.google.cloud.spanner.connection.ITAbstractSpannerTest)15 Statement (com.google.cloud.spanner.Statement)9 AsyncResultSet (com.google.cloud.spanner.AsyncResultSet)7 ApiFuture (com.google.api.core.ApiFuture)6 SettableApiFuture (com.google.api.core.SettableApiFuture)6 ITConnection (com.google.cloud.spanner.connection.ITAbstractSpannerTest.ITConnection)6 Timestamp (com.google.cloud.Timestamp)5 CallbackResponse (com.google.cloud.spanner.AsyncResultSet.CallbackResponse)5 Options (com.google.cloud.spanner.Options)5 SpannerApiFutures.get (com.google.cloud.spanner.SpannerApiFutures.get)5 SpannerExceptionFactory (com.google.cloud.spanner.SpannerExceptionFactory)5 Truth.assertThat (com.google.common.truth.Truth.assertThat)5 CountDownLatch (java.util.concurrent.CountDownLatch)5 ExecutorService (java.util.concurrent.ExecutorService)5 Executors (java.util.concurrent.Executors)5 TimeUnit (java.util.concurrent.TimeUnit)5