use of com.google.cloud.spanner.AbortedDueToConcurrentModificationException in project java-spanner by googleapis.
the class ConnectionAsyncApiAbortedTest method testQueriesAbortedMidway_ResultsChanged.
@Test
public void testQueriesAbortedMidway_ResultsChanged() {
mockSpanner.setExecuteStreamingSqlExecutionTime(SimulatedExecutionTime.ofStreamException(mockSpanner.createAbortedException(ByteString.copyFromUtf8("test")), RANDOM_RESULT_SET_ROW_COUNT - 1));
final Statement statement = Statement.of("SELECT * FROM TEST_TABLE");
final RandomResultSetGenerator generator = new RandomResultSetGenerator(RANDOM_RESULT_SET_ROW_COUNT - 10);
mockSpanner.putStatementResult(StatementResult.query(statement, generator.generate()));
final CountDownLatch latch = new CountDownLatch(1);
final RetryCounter counter = new RetryCounter();
try (Connection connection = createConnection(counter)) {
ApiFuture<Void> res1;
try (AsyncResultSet rs = connection.executeQueryAsync(SELECT_RANDOM_STATEMENT, Options.bufferRows(5))) {
res1 = rs.setCallback(multiThreadedExecutor, resultSet -> {
try {
latch.await(10L, TimeUnit.SECONDS);
while (true) {
switch(resultSet.tryNext()) {
case OK:
break;
case DONE:
return CallbackResponse.DONE;
case NOT_READY:
return CallbackResponse.CONTINUE;
}
}
} catch (Throwable t) {
throw SpannerExceptionFactory.asSpannerException(t);
}
});
}
try (AsyncResultSet rs = connection.executeQueryAsync(statement, Options.bufferRows(5))) {
rs.setCallback(multiThreadedExecutor, new ReadyCallback() {
boolean replaced;
@Override
public CallbackResponse cursorReady(AsyncResultSet resultSet) {
if (!replaced) {
// Replace the result of the query on the server after the first execution.
mockSpanner.putStatementResult(StatementResult.query(statement, generator.generate()));
replaced = true;
}
while (true) {
switch(resultSet.tryNext()) {
case OK:
break;
case DONE:
latch.countDown();
return CallbackResponse.DONE;
case NOT_READY:
return CallbackResponse.CONTINUE;
}
}
}
});
}
try {
get(res1);
fail("Missing expected exception");
} catch (AbortedDueToConcurrentModificationException e) {
assertThat(counter.retryCount).isEqualTo(1);
}
}
}
use of com.google.cloud.spanner.AbortedDueToConcurrentModificationException in project java-spanner by googleapis.
the class ConnectionAsyncApiAbortedTest method testBlindUpdateAborted_WithConcurrentModification.
@Test
public void testBlindUpdateAborted_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);
try {
get(connection.commitAsync());
fail("Missing expected exception");
} catch (AbortedDueToConcurrentModificationException e) {
assertThat(counter.retryCount).isEqualTo(1);
}
}
}
use of com.google.cloud.spanner.AbortedDueToConcurrentModificationException in project java-spanner by googleapis.
the class ITTransactionRetryTest 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.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);
}
}
use of com.google.cloud.spanner.AbortedDueToConcurrentModificationException in project java-spanner by googleapis.
the class ITTransactionRetryTest method testAbortWithCursorHalfwayDroppedTableConcurrentModification.
/**
* Test that shows the following:
*
* <ol>
* <li>Insert two records into table TEST and commit.
* <li>Create the table FOO and insert two test records and commit.
* <li>Query all the records from the TEST table and consume the result set.
* <li>Query all the records from the FOO table and consume only part of the result set.
* <li>Open another connection and drop the table FOO.
* <li>Try to consume the rest of the FOO result set. This aborts.
* <li>The transaction is internally retried. The retry fails as the SELECT statement on FOO
* will now fail.
* </ol>
*/
@Test
public void testAbortWithCursorHalfwayDroppedTableConcurrentModification() {
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')"));
connection2.executeUpdate(Statement.of("INSERT INTO FOO (ID, NAME) VALUES (2, 'test 2')"));
}
try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) {
try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM TEST"))) {
while (rs.next()) {
// do nothing
}
}
// SELECT FROM FOO and consume part of the result set
ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM FOO"));
assertThat(rs.next(), is(true));
// DROP FOO using a different connection
try (ITConnection connection2 = createConnection()) {
connection2.setAutocommit(true);
connection2.execute(Statement.of("DROP TABLE FOO"));
}
// try to continue to consume the result set, but this will now abort.
interceptor.setProbability(1.0);
interceptor.setOnlyInjectOnce(true);
try {
// This will fail as the retry will not succeed.
rs.next();
} catch (AbortedDueToConcurrentModificationException e) {
abortedDueToConcurrentModification = true;
} finally {
rs.close();
}
}
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 testAbortWithDroppedTableConcurrentModification.
/**
* 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.
* <li>Query 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 SELECT statement on FOO
* will now fail.
* </ol>
*/
@Test
public void testAbortWithDroppedTableConcurrentModification() {
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())) {
try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM FOO"))) {
while (rs.next()) {
// do nothing
}
}
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 SELECT *
// FROM 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);
}
Aggregations