Search in sources :

Example 11 with AbortedDueToConcurrentModificationException

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

the class ConnectionAsyncApiAbortedTest method testMultipleBlindUpdatesAborted_WithConcurrentModification.

@Test
public void testMultipleBlindUpdatesAborted_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 the transaction on the next statement. The retry should now fail because of the
        // changed result of the first update.
        mockSpanner.abortNextStatement();
        // Continue to (try to) execute blind updates. This should not cause any exceptions, although
        // all of the returned futures will fail.
        List<ApiFuture<Long>> futures = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            futures.add(connection.executeUpdateAsync(INSERT_STATEMENT));
        }
        for (ApiFuture<Long> fut : futures) {
            try {
                get(fut);
                fail("Missing expected exception");
            } catch (AbortedDueToConcurrentModificationException e) {
                assertThat(counter.retryCount).isEqualTo(1);
            }
        }
    }
}
Also used : ApiFuture(com.google.api.core.ApiFuture) SettableApiFuture(com.google.api.core.SettableApiFuture) Statement(com.google.cloud.spanner.Statement) ITConnection(com.google.cloud.spanner.connection.ITAbstractSpannerTest.ITConnection) ArrayList(java.util.ArrayList) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) Test(org.junit.Test)

Example 12 with AbortedDueToConcurrentModificationException

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

the class ITAsyncTransactionRetryTest method testAbortWithConcurrentUpdate.

@Test
public void testAbortWithConcurrentUpdate() {
    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.executeUpdateAsync(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test 1')"));
        connection.executeUpdateAsync(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (2, 'test 2')"));
        get(connection.commitAsync());
    }
    // 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 (AsyncResultSet rs = connection.executeQueryAsync(Statement.of("SELECT * FROM TEST ORDER BY ID"))) {
            get(rs.setCallback(executor, resultSet -> {
                while (true) {
                    switch(resultSet.tryNext()) {
                        case DONE:
                            return CallbackResponse.DONE;
                        case NOT_READY:
                            return CallbackResponse.CONTINUE;
                        case OK:
                            break;
                    }
                }
            }));
        }
        // open a new connection and transaction and update one of the test records
        try (ITConnection connection2 = createConnection()) {
            connection2.executeUpdateAsync(Statement.of("UPDATE TEST SET NAME='test updated' WHERE ID=2"));
            get(connection2.commitAsync());
        }
        // 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);
        try {
            get(connection.executeUpdateAsync(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (3, 'test 3')")));
            fail("Missing expected exception");
        } catch (AbortedDueToConcurrentModificationException e) {
            assertRetryStatistics(1, 1, 0);
        }
    }
}
Also used : Assume.assumeFalse(org.junit.Assume.assumeFalse) RunWith(org.junit.runner.RunWith) SpannerExceptionFactory(com.google.cloud.spanner.SpannerExceptionFactory) TransactionRetryListener(com.google.cloud.spanner.connection.TransactionRetryListener) Timestamp(com.google.cloud.Timestamp) Connection(com.google.cloud.spanner.connection.Connection) Level(java.util.logging.Level) ResultSet(com.google.cloud.spanner.ResultSet) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) TestName(org.junit.rules.TestName) After(org.junit.After) Assert.fail(org.junit.Assert.fail) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) ExecutorService(java.util.concurrent.ExecutorService) Before(org.junit.Before) AbortedException(com.google.cloud.spanner.AbortedException) AsyncResultSet(com.google.cloud.spanner.AsyncResultSet) ParallelIntegrationTest(com.google.cloud.spanner.ParallelIntegrationTest) AfterClass(org.junit.AfterClass) CallbackResponse(com.google.cloud.spanner.AsyncResultSet.CallbackResponse) SpannerApiFutures.get(com.google.cloud.spanner.SpannerApiFutures.get) EmulatorSpannerHelper.isUsingEmulator(com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator) Test(org.junit.Test) Mutation(com.google.cloud.spanner.Mutation) JUnit4(org.junit.runners.JUnit4) Truth.assertThat(com.google.common.truth.Truth.assertThat) ITAbstractSpannerTest(com.google.cloud.spanner.connection.ITAbstractSpannerTest) Logger(java.util.logging.Logger) Category(org.junit.experimental.categories.Category) Options(com.google.cloud.spanner.Options) Executors(java.util.concurrent.Executors) ApiFuture(com.google.api.core.ApiFuture) SettableApiFuture(com.google.api.core.SettableApiFuture) KeySet(com.google.cloud.spanner.KeySet) ErrorCode(com.google.cloud.spanner.ErrorCode) Statement(com.google.cloud.spanner.Statement) TimeUnit(java.util.concurrent.TimeUnit) CountDownLatch(java.util.concurrent.CountDownLatch) AtomicLong(java.util.concurrent.atomic.AtomicLong) Rule(org.junit.Rule) Struct(com.google.cloud.spanner.Struct) AsyncResultSet(com.google.cloud.spanner.AsyncResultSet) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) ParallelIntegrationTest(com.google.cloud.spanner.ParallelIntegrationTest) Test(org.junit.Test) ITAbstractSpannerTest(com.google.cloud.spanner.connection.ITAbstractSpannerTest)

Example 13 with AbortedDueToConcurrentModificationException

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

the class ITAsyncTransactionRetryTest 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.executeUpdateAsync(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test 1')"));
        connection.executeUpdateAsync(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (2, 'test 2')"));
        get(connection.commitAsync());
    }
    // 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 (AsyncResultSet rs = connection.executeQueryAsync(Statement.of("SELECT * FROM TEST ORDER BY ID"))) {
            get(rs.setCallback(executor, resultSet -> {
                while (true) {
                    switch(resultSet.tryNext()) {
                        case DONE:
                            return CallbackResponse.DONE;
                        case NOT_READY:
                            return CallbackResponse.CONTINUE;
                        case OK:
                            break;
                    }
                }
            }));
        }
        // open a new connection and transaction and remove one of the test records
        try (ITConnection connection2 = createConnection()) {
            connection2.executeUpdateAsync(Statement.of("DELETE FROM TEST WHERE ID=1"));
            get(connection2.commitAsync());
        }
        // 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);
        try {
            get(connection.executeUpdateAsync(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (3, 'test 3')")));
            fail("Missing expected exception");
        } catch (AbortedDueToConcurrentModificationException e) {
            assertRetryStatistics(1, 1, 0);
        }
    }
}
Also used : Assume.assumeFalse(org.junit.Assume.assumeFalse) RunWith(org.junit.runner.RunWith) SpannerExceptionFactory(com.google.cloud.spanner.SpannerExceptionFactory) TransactionRetryListener(com.google.cloud.spanner.connection.TransactionRetryListener) Timestamp(com.google.cloud.Timestamp) Connection(com.google.cloud.spanner.connection.Connection) Level(java.util.logging.Level) ResultSet(com.google.cloud.spanner.ResultSet) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) TestName(org.junit.rules.TestName) After(org.junit.After) Assert.fail(org.junit.Assert.fail) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) ExecutorService(java.util.concurrent.ExecutorService) Before(org.junit.Before) AbortedException(com.google.cloud.spanner.AbortedException) AsyncResultSet(com.google.cloud.spanner.AsyncResultSet) ParallelIntegrationTest(com.google.cloud.spanner.ParallelIntegrationTest) AfterClass(org.junit.AfterClass) CallbackResponse(com.google.cloud.spanner.AsyncResultSet.CallbackResponse) SpannerApiFutures.get(com.google.cloud.spanner.SpannerApiFutures.get) EmulatorSpannerHelper.isUsingEmulator(com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator) Test(org.junit.Test) Mutation(com.google.cloud.spanner.Mutation) JUnit4(org.junit.runners.JUnit4) Truth.assertThat(com.google.common.truth.Truth.assertThat) ITAbstractSpannerTest(com.google.cloud.spanner.connection.ITAbstractSpannerTest) Logger(java.util.logging.Logger) Category(org.junit.experimental.categories.Category) Options(com.google.cloud.spanner.Options) Executors(java.util.concurrent.Executors) ApiFuture(com.google.api.core.ApiFuture) SettableApiFuture(com.google.api.core.SettableApiFuture) KeySet(com.google.cloud.spanner.KeySet) ErrorCode(com.google.cloud.spanner.ErrorCode) Statement(com.google.cloud.spanner.Statement) TimeUnit(java.util.concurrent.TimeUnit) CountDownLatch(java.util.concurrent.CountDownLatch) AtomicLong(java.util.concurrent.atomic.AtomicLong) Rule(org.junit.Rule) Struct(com.google.cloud.spanner.Struct) AsyncResultSet(com.google.cloud.spanner.AsyncResultSet) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) ParallelIntegrationTest(com.google.cloud.spanner.ParallelIntegrationTest) Test(org.junit.Test) ITAbstractSpannerTest(com.google.cloud.spanner.connection.ITAbstractSpannerTest)

Example 14 with AbortedDueToConcurrentModificationException

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

the class ITAsyncTransactionRetryTest method testAbortWithConcurrentInsert.

@Test
public void testAbortWithConcurrentInsert() {
    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.executeUpdateAsync(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test 1')"));
        connection.executeUpdateAsync(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (2, 'test 2')"));
        // select the test records and consume the entire result set
        try (AsyncResultSet rs = connection.executeQueryAsync(Statement.of("SELECT * FROM TEST ORDER BY ID"))) {
            get(rs.setCallback(executor, resultSet -> {
                while (true) {
                    switch(resultSet.tryNext()) {
                        case DONE:
                            return CallbackResponse.DONE;
                        case NOT_READY:
                            return CallbackResponse.CONTINUE;
                        case OK:
                            break;
                    }
                }
            }));
        }
        // open a new connection and transaction and do an additional insert
        try (ITConnection connection2 = createConnection()) {
            connection2.executeUpdateAsync(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (3, 'test 3')"));
            get(connection2.commitAsync());
        }
        // 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);
        ApiFuture<Long> updateCount = connection.executeUpdateAsync(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (4, 'test 4')"));
        try {
            get(updateCount);
            fail("Missing expected exception");
        } catch (AbortedDueToConcurrentModificationException e) {
            assertRetryStatistics(1, 1, 0);
        }
    }
}
Also used : Assume.assumeFalse(org.junit.Assume.assumeFalse) RunWith(org.junit.runner.RunWith) SpannerExceptionFactory(com.google.cloud.spanner.SpannerExceptionFactory) TransactionRetryListener(com.google.cloud.spanner.connection.TransactionRetryListener) Timestamp(com.google.cloud.Timestamp) Connection(com.google.cloud.spanner.connection.Connection) Level(java.util.logging.Level) ResultSet(com.google.cloud.spanner.ResultSet) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) TestName(org.junit.rules.TestName) After(org.junit.After) Assert.fail(org.junit.Assert.fail) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) ExecutorService(java.util.concurrent.ExecutorService) Before(org.junit.Before) AbortedException(com.google.cloud.spanner.AbortedException) AsyncResultSet(com.google.cloud.spanner.AsyncResultSet) ParallelIntegrationTest(com.google.cloud.spanner.ParallelIntegrationTest) AfterClass(org.junit.AfterClass) CallbackResponse(com.google.cloud.spanner.AsyncResultSet.CallbackResponse) SpannerApiFutures.get(com.google.cloud.spanner.SpannerApiFutures.get) EmulatorSpannerHelper.isUsingEmulator(com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator) Test(org.junit.Test) Mutation(com.google.cloud.spanner.Mutation) JUnit4(org.junit.runners.JUnit4) Truth.assertThat(com.google.common.truth.Truth.assertThat) ITAbstractSpannerTest(com.google.cloud.spanner.connection.ITAbstractSpannerTest) Logger(java.util.logging.Logger) Category(org.junit.experimental.categories.Category) Options(com.google.cloud.spanner.Options) Executors(java.util.concurrent.Executors) ApiFuture(com.google.api.core.ApiFuture) SettableApiFuture(com.google.api.core.SettableApiFuture) KeySet(com.google.cloud.spanner.KeySet) ErrorCode(com.google.cloud.spanner.ErrorCode) Statement(com.google.cloud.spanner.Statement) TimeUnit(java.util.concurrent.TimeUnit) CountDownLatch(java.util.concurrent.CountDownLatch) AtomicLong(java.util.concurrent.atomic.AtomicLong) Rule(org.junit.Rule) Struct(com.google.cloud.spanner.Struct) AsyncResultSet(com.google.cloud.spanner.AsyncResultSet) AtomicLong(java.util.concurrent.atomic.AtomicLong) AbortedDueToConcurrentModificationException(com.google.cloud.spanner.AbortedDueToConcurrentModificationException) ParallelIntegrationTest(com.google.cloud.spanner.ParallelIntegrationTest) Test(org.junit.Test) ITAbstractSpannerTest(com.google.cloud.spanner.connection.ITAbstractSpannerTest)

Example 15 with AbortedDueToConcurrentModificationException

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

the class ITSqlMusicScriptTest method test02_RunAbortedTest.

@Test
public void test02_RunAbortedTest() {
    assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator());
    final long SINGER_ID = 2L;
    final long VENUE_ID = 68L;
    final long NUMBER_OF_SINGERS = 30L;
    final long NUMBER_OF_ALBUMS = 60L;
    final long NUMBER_OF_SONGS = 149L;
    final long NUMBER_OF_CONCERTS = 100L;
    long numberOfSongs = 0L;
    AbortInterceptor interceptor = new AbortInterceptor(0.0D);
    try (ITConnection connection = createConnection(interceptor)) {
        connection.setAutocommit(false);
        connection.setRetryAbortsInternally(true);
        // Read all data from the different music tables in the transaction
        // The previous test deleted the first two Singers records.
        long expectedId = 3L;
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM Singers ORDER BY SingerId"))) {
            while (rs.next()) {
                assertThat(rs.getLong("SingerId"), is(equalTo(expectedId)));
                expectedId++;
            }
        }
        assertThat(expectedId, is(equalTo(NUMBER_OF_SINGERS + 1L)));
        expectedId = 3L;
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM Albums ORDER BY AlbumId"))) {
            while (rs.next()) {
                assertThat(rs.getLong("AlbumId"), is(equalTo(expectedId)));
                expectedId++;
                // 31 and 32 were deleted by the first test script.
                if (expectedId == 31L || expectedId == 32L) {
                    expectedId = 33L;
                }
            }
        }
        assertThat(expectedId, is(equalTo(NUMBER_OF_ALBUMS + 1L)));
        expectedId = 1L;
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM Songs ORDER BY TrackId"))) {
            while (rs.next()) {
                assertThat(rs.getLong("TrackId"), is(equalTo(expectedId)));
                expectedId++;
                numberOfSongs++;
                // 40, 64, 76, 86 and 96 were deleted by the first test script.
                if (expectedId == 40L || expectedId == 64L || expectedId == 76L || expectedId == 86L || expectedId == 96L) {
                    expectedId++;
                }
            }
        }
        assertThat(expectedId, is(equalTo(NUMBER_OF_SONGS + 1L)));
        // Concerts are not in the table hierarchy, so no records have been deleted.
        expectedId = 1L;
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM Concerts ORDER BY VenueId"))) {
            while (rs.next()) {
                assertThat(rs.getLong("VenueId"), is(equalTo(expectedId)));
                expectedId++;
            }
        }
        assertThat(expectedId, is(equalTo(NUMBER_OF_CONCERTS + 1L)));
        // make one small concurrent change in a different transaction
        List<Long> originalPrices;
        List<Long> newPrices;
        try (ITConnection connection2 = createConnection()) {
            assertThat(connection2.isAutocommit(), is(true));
            try (ResultSet rs = connection2.executeQuery(Statement.newBuilder("SELECT TicketPrices FROM Concerts WHERE SingerId=@singer AND VenueId=@venue").bind("singer").to(SINGER_ID).bind("venue").to(VENUE_ID).build())) {
                assertThat(rs.next(), is(true));
                originalPrices = rs.getLongList(0);
                // increase one of the prices by 1
                newPrices = new ArrayList<>(originalPrices);
                newPrices.set(1, originalPrices.get(1) + 1);
                connection2.executeUpdate(Statement.newBuilder("UPDATE Concerts SET TicketPrices=@prices WHERE SingerId=@singer AND VenueId=@venue").bind("prices").toInt64Array(newPrices).bind("singer").to(SINGER_ID).bind("venue").to(VENUE_ID).build());
            }
        }
        // try to add a new song and then try to commit, but trigger an abort on commit
        connection.bufferedWrite(Mutation.newInsertBuilder("Songs").set("SingerId").to(3L).set("AlbumId").to(3L).set("TrackId").to(1L).set("SongName").to("Aborted").set("Duration").to(1L).set("SongGenre").to("Unknown").build());
        interceptor.setProbability(1.0);
        interceptor.setOnlyInjectOnce(true);
        // the transaction retry should fail because of the concurrent modification
        boolean expectedException = false;
        try {
            connection.commit();
        } catch (AbortedDueToConcurrentModificationException e) {
            expectedException = true;
        }
        // verify that the commit aborted, an internal retry was started and then aborted because of
        // the concurrent modification
        assertThat(expectedException, is(true));
        // Rollback the transaction to start a new one.
        connection.rollback();
        // verify that the prices were changed
        try (ResultSet rs = connection.executeQuery(Statement.newBuilder("SELECT TicketPrices FROM Concerts WHERE SingerId=@singer AND VenueId=@venue").bind("singer").to(SINGER_ID).bind("venue").to(VENUE_ID).build())) {
            assertThat(rs.next(), is(true));
            assertThat(rs.getLongList(0), is(equalTo(newPrices)));
        }
        // verify that the new song was not written to the database
        try (ResultSet rs = connection.executeQuery(Statement.of("SELECT COUNT(*) FROM Songs"))) {
            assertThat(rs.next(), is(true));
            assertThat(rs.getLong(0), is(equalTo(numberOfSongs)));
        }
    }
}
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