Skip to content

Commit 78470cb

Browse files
test: add a test for blind writes (#2595)
* test: add a test for blind writes Adds a test for blind writes before executing a read or SQL statement in a read/write transaction. * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 465df7b commit 78470cb

File tree

1 file changed

+50
-0
lines changed

1 file changed

+50
-0
lines changed

google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import java.util.concurrent.ScheduledExecutorService;
7070
import java.util.concurrent.ScheduledThreadPoolExecutor;
7171
import java.util.concurrent.atomic.AtomicBoolean;
72+
import java.util.concurrent.atomic.AtomicInteger;
7273
import org.junit.After;
7374
import org.junit.AfterClass;
7475
import org.junit.Before;
@@ -1359,6 +1360,55 @@ public void testInlinedBeginTxWithOnlyMutations() {
13591360
assertThat(countTransactionsStarted()).isEqualTo(1);
13601361
}
13611362

1363+
@Test
1364+
public void testInlinedBeginTxWithMutationsBeforeFailedSqlStatement() {
1365+
Statement insert = Statement.of("insert into foo (id) values (1)");
1366+
Statement update = Statement.of("update foo set value='Two' where id=2");
1367+
mockSpanner.putStatementResult(StatementResult.update(insert, 1L));
1368+
mockSpanner.putStatementResult(StatementResult.update(update, 1L));
1369+
// This error will be returned the first time the ExecuteSql method is called. The error is
1370+
// cleared after the first call, meaning that the second attempt will succeed.
1371+
mockSpanner.setExecuteSqlExecutionTime(
1372+
SimulatedExecutionTime.ofException(Status.ALREADY_EXISTS.asRuntimeException()));
1373+
1374+
DatabaseClient client =
1375+
spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]"));
1376+
AtomicInteger attempts = new AtomicInteger();
1377+
client
1378+
.readWriteTransaction()
1379+
.run(
1380+
transaction -> {
1381+
attempts.incrementAndGet();
1382+
// Buffer a blind write before executing a SQL statement.
1383+
transaction.buffer(
1384+
Collections.singletonList(
1385+
Mutation.newInsertBuilder("FOO").set("ID").to(1L).build()));
1386+
try {
1387+
transaction.executeUpdate(insert);
1388+
} catch (SpannerException exception) {
1389+
assertEquals(ErrorCode.ALREADY_EXISTS, exception.getErrorCode());
1390+
// The error should only occur during the initial attempt.
1391+
assertEquals(1, attempts.get());
1392+
}
1393+
// We need to execute one more statement in the transaction in order to force a
1394+
// retry.
1395+
assertEquals(1L, transaction.executeUpdate(update));
1396+
return null;
1397+
});
1398+
// The transaction should be retried once.
1399+
assertEquals(2, attempts.get());
1400+
assertEquals(1, mockSpanner.countRequestsOfType(BeginTransactionRequest.class));
1401+
// We get 3 ExecuteSql requests:
1402+
// 1. The initial attempt that carries a BeginTransaction option.
1403+
// 2. The retry attempt that does not use a BeginTransaction option.
1404+
// 3. The second UPDATE statement in the transaction that is only executed during the retry.
1405+
assertEquals(3, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
1406+
assertEquals(1, mockSpanner.countRequestsOfType(CommitRequest.class));
1407+
CommitRequest commit = mockSpanner.getRequestsOfType(CommitRequest.class).get(0);
1408+
// The mutations should only be applied once.
1409+
assertEquals(1, commit.getMutationsCount());
1410+
}
1411+
13621412
@SuppressWarnings("resource")
13631413
@Test
13641414
public void testTransactionManagerInlinedBeginTx() {

0 commit comments

Comments
 (0)