Search in sources :

Example 16 with AbortedDueToConcurrentModificationException

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

the class ITTransactionRetryTest method testAbortWithConcurrentDelete.

@Test
public void testAbortWithConcurrentDelete() {
    assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator());
    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();
    }
    // open a new connection and select the two test records
    try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) {
        // select the test records and consume the entire result set
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM TEST ORDER BY ID"))) {
            while (rs.next()) {
            // do nothing
            }
        }
        // open a new connection and transaction and remove one of the test records
        try (ITConnection connection2 = createConnection()) {
            connection2.executeUpdate(Statement.of("DELETE FROM TEST WHERE ID=1"));
            connection2.commit();
        }
        // now try to do an insert that will abort. The retry should now fail as there has been a
        // concurrent modification
        interceptor.setProbability(1.0);
        interceptor.setOnlyInjectOnce(true);
        boolean expectedException = false;
        try {
            connection.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (3, 'test 3')"));
        } catch (AbortedDueToConcurrentModificationException e) {
            expectedException = true;
        }
        assertThat(expectedException, is(true));
        assertRetryStatistics(1, 1, 0);
    }
}
Also used : ResultSet(com.google.cloud.spanner.ResultSet) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) ParallelIntegrationTest(com.google.cloud.spanner.ParallelIntegrationTest) Test(org.junit.Test) ITAbstractSpannerTest(com.google.cloud.spanner.connection.ITAbstractSpannerTest)

Example 17 with AbortedDueToConcurrentModificationException

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

the class ITTransactionRetryTest method testNestedAbortWithConcurrentInsert.

/**
 * Test that shows the following:
 *
 * <ol>
 *   <li>Transaction 1 does two inserts in table TEST
 *   <li>Transaction 1 selects all records from table TEST
 *   <li>Transaction 2 inserts a record into TEST
 *   <li>Transaction 1 does another insert into TEST that aborts
 *   <li>Transaction 1 starts a retry that aborts at the SELECT statement (i.e. before the
 *       concurrent modification has been seen)
 *   <li>Transaction 1 restarts the retry that now aborts due to a concurrent modification
 *       exception
 * </ol>
 */
@Test
public void testNestedAbortWithConcurrentInsert() {
    assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator());
    AbortInterceptor interceptor = new AbortInterceptor(0) {

        private boolean alreadyAborted = false;

        @Override
        protected boolean shouldAbort(String statement, ExecutionStep step) {
            // Abort during retry on the select statement.
            if (!alreadyAborted && statement.equals("SELECT * FROM TEST ORDER BY ID") && step == ExecutionStep.RETRY_STATEMENT) {
                alreadyAborted = true;
                return true;
            }
            return super.shouldAbort(statement, step);
        }
    };
    try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) {
        // insert two test records
        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')"));
        // select the test records and consume the entire result set
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM TEST ORDER BY ID"))) {
            while (rs.next()) {
            // do nothing
            }
        }
        // open a new connection and transaction and do an additional insert
        try (ITConnection connection2 = createConnection()) {
            connection2.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (3, 'test 3')"));
            connection2.commit();
        }
        // Now try to do an insert that will abort. The retry should now fail as there has been a
        // concurrent modification.
        interceptor.setProbability(1.0);
        interceptor.setOnlyInjectOnce(true);
        boolean expectedException = false;
        try {
            connection.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (4, 'test 4')"));
        } catch (AbortedDueToConcurrentModificationException e) {
            expectedException = true;
        }
        assertThat(expectedException, is(true));
        assertRetryStatistics(2, 1, 0);
        assertThat(RETRY_STATISTICS.totalNestedAborts > 0, is(true));
    }
}
Also used : ResultSet(com.google.cloud.spanner.ResultSet) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) ParallelIntegrationTest(com.google.cloud.spanner.ParallelIntegrationTest) Test(org.junit.Test) ITAbstractSpannerTest(com.google.cloud.spanner.connection.ITAbstractSpannerTest)

Example 18 with AbortedDueToConcurrentModificationException

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

the class ITTransactionRetryTest method testAbortWithConcurrentInsertAndContinue.

/**
 * Test that shows that a transaction that has aborted is considered to be rolled back, and new
 * statements will be executed in a new transaction
 */
@Test
public void testAbortWithConcurrentInsertAndContinue() {
    assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator());
    AbortInterceptor interceptor = new AbortInterceptor(0);
    try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) {
        // insert two test records
        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')"));
        // Select the test records and consume the entire result set.
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM TEST ORDER BY ID"))) {
            while (rs.next()) {
            // do nothing
            }
        }
        // Open a new connection and transaction and do an additional insert
        try (ITConnection connection2 = createConnection()) {
            connection2.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (3, 'test 3')"));
            connection2.commit();
        }
        // Now try to do an insert that will abort. The retry should now fail as there has been a
        // concurrent modification.
        interceptor.setProbability(1.0);
        interceptor.setOnlyInjectOnce(true);
        boolean expectedException = false;
        try {
            connection.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (4, 'test 4')"));
        } catch (AbortedDueToConcurrentModificationException e) {
            expectedException = true;
        }
        assertThat(expectedException, is(true));
        assertRetryStatistics(1, 1, 0);
        // Rollback the aborted transaction to start a new one.
        connection.rollback();
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM TEST"))) {
            // there should be one record from the transaction on connection2
            assertThat(rs.next(), is(true));
            assertThat(rs.next(), is(false));
        }
    }
}
Also used : ResultSet(com.google.cloud.spanner.ResultSet) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) ParallelIntegrationTest(com.google.cloud.spanner.ParallelIntegrationTest) Test(org.junit.Test) ITAbstractSpannerTest(com.google.cloud.spanner.connection.ITAbstractSpannerTest)

Example 19 with AbortedDueToConcurrentModificationException

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

the class ITTransactionRetryTest method testAbortWithInsertOnDroppedTableConcurrentModification.

/**
 * Test that shows the following:
 *
 * <ol>
 *   <li>Insert two records into table TEST and commit.
 *   <li>Create the table FOO and insert a test record and commit.
 *   <li>Insert another record into the table FOO.
 *   <li>Query all the records from the TEST table and consume the result set.
 *   <li>Open another connection and drop 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 fail.
 * </ol>
 */
@Test
public void testAbortWithInsertOnDroppedTableConcurrentModification() {
    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();
    }
    // CREATE FOO
    try (ITConnection connection2 = createConnection()) {
        connection2.setAutocommit(true);
        connection2.execute(Statement.of("CREATE TABLE FOO (ID INT64, NAME STRING(100)) PRIMARY KEY (ID)"));
        connection2.executeUpdate(Statement.of("INSERT INTO FOO (ID, NAME) VALUES (1, 'test 1')"));
    }
    try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) {
        // insert a record into FOO
        connection.executeUpdate(Statement.of("INSERT INTO FOO (ID, NAME) VALUES (2, 'test 2')"));
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM TEST"))) {
            while (rs.next()) {
            // do nothing
            }
        }
        // DROP FOO using a different connection
        try (ITConnection connection2 = createConnection()) {
            connection2.setAutocommit(true);
            connection2.execute(Statement.of("DROP TABLE FOO"));
        }
        // Now try to do an insert that will abort. The subsequent retry will fail as the INSERT INTO
        // FOO now fails.
        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;
        }
    }
    assertThat(abortedDueToConcurrentModification, is(true));
    assertRetryStatistics(1, 1, 0);
}
Also used : ResultSet(com.google.cloud.spanner.ResultSet) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) ParallelIntegrationTest(com.google.cloud.spanner.ParallelIntegrationTest) Test(org.junit.Test) ITAbstractSpannerTest(com.google.cloud.spanner.connection.ITAbstractSpannerTest)

Example 20 with AbortedDueToConcurrentModificationException

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

the class ITTransactionRetryTest method testAbortWithConcurrentInsertOnEmptyTable.

@Test
public void testAbortWithConcurrentInsertOnEmptyTable() {
    assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator());
    AbortInterceptor interceptor = new AbortInterceptor(0);
    try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) {
        // select the test records but do not consume the result set
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM TEST ORDER BY ID"))) {
            // hence is not a problem for retrying the transaction.
            try (ITConnection connection2 = createConnection()) {
                connection2.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test 1')"));
                connection2.commit();
            }
            // Now try to consume the result set, but the call to next() will throw an AbortedException.
            // The retry should still succeed.
            interceptor.setProbability(1.0);
            interceptor.setOnlyInjectOnce(true);
            int currentSuccessfulRetryCount = RETRY_STATISTICS.totalSuccessfulRetries;
            assertThat(rs.next(), is(true));
            assertThat(RETRY_STATISTICS.totalSuccessfulRetries, is(equalTo(currentSuccessfulRetryCount + 1)));
            assertThat(rs.next(), is(false));
        }
        connection.commit();
        // Now do the same, but this time we will consume the empty result set. The retry should now
        // fail.
        clearTable();
        clearStatistics();
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM TEST ORDER BY ID"))) {
            assertThat(rs.next(), is(false));
            // included in a retry of the above query, and this time it will cause the retry to fail.
            try (ITConnection connection2 = createConnection()) {
                connection2.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test 1')"));
                connection2.commit();
            }
            // this time the abort will occur on the call to commit()
            interceptor.setProbability(1.0);
            interceptor.setOnlyInjectOnce(true);
            boolean expectedException = false;
            try {
                connection.commit();
            } catch (AbortedDueToConcurrentModificationException e) {
                expectedException = true;
            }
            // No successful retries.
            assertRetryStatistics(1, 1, 0);
            assertThat(expectedException, is(true));
        }
    }
}
Also used : ResultSet(com.google.cloud.spanner.ResultSet) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) ParallelIntegrationTest(com.google.cloud.spanner.ParallelIntegrationTest) Test(org.junit.Test) ITAbstractSpannerTest(com.google.cloud.spanner.connection.ITAbstractSpannerTest)

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