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);
}
}
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));
}
}
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));
}
}
}
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);
}
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));
}
}
}
Aggregations