From 93901870b0866adb3f628f2cf19febf37a4e781f Mon Sep 17 00:00:00 2001 From: Sakthivel Subramanian Date: Wed, 9 Jul 2025 13:38:47 +0530 Subject: [PATCH 1/6] ci: Add tests for retry_delay trailer --- .../cloud/spanner/ITTransactionRetryTest.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java new file mode 100644 index 00000000000..2d59ed03dbf --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java @@ -0,0 +1,52 @@ +package com.google.cloud.spanner; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertTrue; + +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@Category(ParallelIntegrationTest.class) +@RunWith(JUnit4.class) +public class ITTransactionRetryTest { + + @ClassRule + public static IntegrationTestEnv env = new IntegrationTestEnv(); + + @Test + public void TestRetryInfo() { + // Creating a database with the table which contains INT64 columns + Database db = env.getTestHelper().createTestDatabase("CREATE TABLE Test(ID INT64, " + + "EMPID INT64) PRIMARY KEY (ID)"); + DatabaseClient databaseClient = env.getTestHelper().getClient().getDatabaseClient(db.getId()); + + // Inserting one row + databaseClient.readWriteTransaction().run(transaction -> { + transaction.buffer(Mutation.newInsertBuilder("Test") + .set("ID") + .to(1) + .set("EMPID") + .to(1) + .build()); + return null; + }); + + try(TransactionManager transactionManager1 = databaseClient.transactionManager()) { + try(TransactionManager transactionManager2 = databaseClient.transactionManager()) { + TransactionContext transaction1 = transactionManager1.begin(); + TransactionContext transaction2 = transactionManager2.begin(); + transaction1.executeUpdate(Statement.of("UPDATE Test SET EMPID = EMPID + 1 WHERE ID = 1")); + transaction2.executeUpdate(Statement.of("UPDATE Test SET EMPID = EMPID + 1 WHERE ID = 1")); + transactionManager1.commit(); + AbortedException abortedException = Assert.assertThrows(AbortedException.class, + transactionManager2::commit); + assertThat(abortedException.getErrorCode()).isEqualTo(ErrorCode.ABORTED); + assertTrue(abortedException.getRetryDelayInMillis() > 0); + } + } + } +} From 2b9ec13c54df43f49af295cde31f98eafd4d9a57 Mon Sep 17 00:00:00 2001 From: Sakthivel Subramanian Date: Wed, 9 Jul 2025 13:44:57 +0530 Subject: [PATCH 2/6] add license header --- .../cloud/spanner/ITTransactionRetryTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java index 2d59ed03dbf..d5d17bc6776 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.cloud.spanner; import static com.google.common.truth.Truth.assertThat; From 8fa2721a779506b25327a901f349a9071ab1a049 Mon Sep 17 00:00:00 2001 From: cloud-java-bot Date: Wed, 9 Jul 2025 08:18:01 +0000 Subject: [PATCH 3/6] chore: generate libraries at Wed Jul 9 08:15:27 UTC 2025 --- .../cloud/spanner/ITTransactionRetryTest.java | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java index d5d17bc6776..942c1b3e9c7 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java @@ -30,36 +30,35 @@ @RunWith(JUnit4.class) public class ITTransactionRetryTest { - @ClassRule - public static IntegrationTestEnv env = new IntegrationTestEnv(); + @ClassRule public static IntegrationTestEnv env = new IntegrationTestEnv(); @Test public void TestRetryInfo() { // Creating a database with the table which contains INT64 columns - Database db = env.getTestHelper().createTestDatabase("CREATE TABLE Test(ID INT64, " - + "EMPID INT64) PRIMARY KEY (ID)"); + Database db = + env.getTestHelper() + .createTestDatabase("CREATE TABLE Test(ID INT64, " + "EMPID INT64) PRIMARY KEY (ID)"); DatabaseClient databaseClient = env.getTestHelper().getClient().getDatabaseClient(db.getId()); // Inserting one row - databaseClient.readWriteTransaction().run(transaction -> { - transaction.buffer(Mutation.newInsertBuilder("Test") - .set("ID") - .to(1) - .set("EMPID") - .to(1) - .build()); - return null; - }); + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.buffer( + Mutation.newInsertBuilder("Test").set("ID").to(1).set("EMPID").to(1).build()); + return null; + }); - try(TransactionManager transactionManager1 = databaseClient.transactionManager()) { - try(TransactionManager transactionManager2 = databaseClient.transactionManager()) { + try (TransactionManager transactionManager1 = databaseClient.transactionManager()) { + try (TransactionManager transactionManager2 = databaseClient.transactionManager()) { TransactionContext transaction1 = transactionManager1.begin(); TransactionContext transaction2 = transactionManager2.begin(); transaction1.executeUpdate(Statement.of("UPDATE Test SET EMPID = EMPID + 1 WHERE ID = 1")); transaction2.executeUpdate(Statement.of("UPDATE Test SET EMPID = EMPID + 1 WHERE ID = 1")); transactionManager1.commit(); - AbortedException abortedException = Assert.assertThrows(AbortedException.class, - transactionManager2::commit); + AbortedException abortedException = + Assert.assertThrows(AbortedException.class, transactionManager2::commit); assertThat(abortedException.getErrorCode()).isEqualTo(ErrorCode.ABORTED); assertTrue(abortedException.getRetryDelayInMillis() > 0); } From 04cb76eefdcef7cbd72aa31a79f34632274a49c7 Mon Sep 17 00:00:00 2001 From: Sakthivel Subramanian Date: Wed, 9 Jul 2025 13:59:10 +0530 Subject: [PATCH 4/6] skip the test for emulator --- .../java/com/google/cloud/spanner/ITTransactionRetryTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java index 942c1b3e9c7..c9a585a3cee 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java @@ -16,8 +16,10 @@ package com.google.cloud.spanner; +import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; import org.junit.Assert; import org.junit.ClassRule; @@ -34,6 +36,8 @@ public class ITTransactionRetryTest { @Test public void TestRetryInfo() { + assumeFalse("emulator does not support parallel transaction", isUsingEmulator()); + // Creating a database with the table which contains INT64 columns Database db = env.getTestHelper() From 9903cdad7128b5a1c27309b729c9c7dd5df14b35 Mon Sep 17 00:00:00 2001 From: Sakthivel Subramanian Date: Wed, 9 Jul 2025 14:25:56 +0530 Subject: [PATCH 5/6] Address review comments --- .../com/google/cloud/spanner/ITTransactionRetryTest.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java index c9a585a3cee..016a6100990 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java @@ -60,9 +60,14 @@ public void TestRetryInfo() { TransactionContext transaction2 = transactionManager2.begin(); transaction1.executeUpdate(Statement.of("UPDATE Test SET EMPID = EMPID + 1 WHERE ID = 1")); transaction2.executeUpdate(Statement.of("UPDATE Test SET EMPID = EMPID + 1 WHERE ID = 1")); - transactionManager1.commit(); + AbortedException abortedException = - Assert.assertThrows(AbortedException.class, transactionManager2::commit); + Assert.assertThrows( + AbortedException.class, + () -> { + transactionManager1.commit(); + transactionManager2.commit(); + }); assertThat(abortedException.getErrorCode()).isEqualTo(ErrorCode.ABORTED); assertTrue(abortedException.getRetryDelayInMillis() > 0); } From 5cbddebfca217d1c343cb5be34fa603d9ba46a99 Mon Sep 17 00:00:00 2001 From: Sakthivel Subramanian Date: Wed, 9 Jul 2025 18:46:13 +0530 Subject: [PATCH 6/6] Address review comments --- .../cloud/spanner/ITTransactionRetryTest.java | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java index 016a6100990..da48f661790 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITTransactionRetryTest.java @@ -21,7 +21,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; -import org.junit.Assert; import org.junit.ClassRule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -54,23 +53,31 @@ public void TestRetryInfo() { return null; }); - try (TransactionManager transactionManager1 = databaseClient.transactionManager()) { - try (TransactionManager transactionManager2 = databaseClient.transactionManager()) { - TransactionContext transaction1 = transactionManager1.begin(); - TransactionContext transaction2 = transactionManager2.begin(); - transaction1.executeUpdate(Statement.of("UPDATE Test SET EMPID = EMPID + 1 WHERE ID = 1")); - transaction2.executeUpdate(Statement.of("UPDATE Test SET EMPID = EMPID + 1 WHERE ID = 1")); - - AbortedException abortedException = - Assert.assertThrows( - AbortedException.class, - () -> { - transactionManager1.commit(); - transactionManager2.commit(); - }); - assertThat(abortedException.getErrorCode()).isEqualTo(ErrorCode.ABORTED); - assertTrue(abortedException.getRetryDelayInMillis() > 0); + int numRetries = 10; + boolean isAbortedWithRetryInfo = false; + while (numRetries-- > 0) { + try (TransactionManager transactionManager1 = databaseClient.transactionManager()) { + try (TransactionManager transactionManager2 = databaseClient.transactionManager()) { + try { + TransactionContext transaction1 = transactionManager1.begin(); + TransactionContext transaction2 = transactionManager2.begin(); + transaction1.executeUpdate( + Statement.of("UPDATE Test SET EMPID = EMPID + 1 WHERE ID = 1")); + transaction2.executeUpdate( + Statement.of("UPDATE Test SET EMPID = EMPID + 1 WHERE ID = 1")); + transactionManager1.commit(); + transactionManager2.commit(); + } catch (AbortedException abortedException) { + assertThat(abortedException.getErrorCode()).isEqualTo(ErrorCode.ABORTED); + if (abortedException.getRetryDelayInMillis() > 0) { + isAbortedWithRetryInfo = true; + break; + } + } + } } } + + assertTrue("Transaction is not aborted with the trailers", isAbortedWithRetryInfo); } }