From 87f1b5cf563a3288f8ee54cc4a766572c2f887f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Fri, 15 Sep 2023 15:28:20 +0200 Subject: [PATCH 1/3] fix: retry aborted errors for writeAtLeastOnce The `writeAtLeastOnce` method could fail with an Aborted error if Cloud Spanner would abort the transaction during the single Commit RPC invocation that this method executes. This can for example happen if a schema change is executed while this method is being called. Fixes #2626 --- .../com/google/cloud/spanner/SessionImpl.java | 6 +-- .../cloud/spanner/DatabaseClientImplTest.java | 38 +++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java index 2bef8e3ada4..1f37a849b3b 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java @@ -182,11 +182,11 @@ public CommitResponse writeAtLeastOnceWithOptions( } requestBuilder.setRequestOptions(requestOptionsBuilder.build()); } + CommitRequest request = requestBuilder.build(); Span span = tracer.spanBuilder(SpannerImpl.COMMIT).startSpan(); try (Scope s = tracer.withSpan(span)) { - com.google.spanner.v1.CommitResponse response = - spanner.getRpc().commit(requestBuilder.build(), this.options); - return new CommitResponse(response); + return SpannerRetryHelper.runTxWithRetriesOnAborted( + () -> new CommitResponse(spanner.getRpc().commit(request, this.options))); } catch (RuntimeException e) { TraceUtil.setWithFailure(span, e); throw e; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java index 250864f0baf..feb0f4b23c6 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java @@ -525,6 +525,44 @@ public void testWrite() { assertEquals(Priority.PRIORITY_UNSPECIFIED, commit.getRequestOptions().getPriority()); } + @Test + public void testWriteAborted() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + // Force the Commit RPC to return Aborted the first time it is called. The exception is cleared + // after the first call, so the retry should succeed. + mockSpanner.setCommitExecutionTime( + SimulatedExecutionTime.ofException( + mockSpanner.createAbortedException(ByteString.copyFromUtf8("test")))); + Timestamp timestamp = + client.write( + Collections.singletonList( + Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build())); + assertNotNull(timestamp); + + List commitRequests = mockSpanner.getRequestsOfType(CommitRequest.class); + assertEquals(2, commitRequests.size()); + } + + @Test + public void testWriteAtLeastOnceAborted() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + // Force the Commit RPC to return Aborted the first time it is called. The exception is cleared + // after the first call, so the retry should succeed. + mockSpanner.setCommitExecutionTime( + SimulatedExecutionTime.ofException( + mockSpanner.createAbortedException(ByteString.copyFromUtf8("test")))); + Timestamp timestamp = + client.writeAtLeastOnce( + Collections.singletonList( + Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build())); + assertNotNull(timestamp); + + List commitRequests = mockSpanner.getRequestsOfType(CommitRequest.class); + assertEquals(2, commitRequests.size()); + } + @Test public void testWriteWithOptions() { DatabaseClient client = From 63c33185672317e3387f586e4c4fd88826b4bc22 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 15 Sep 2023 13:32:48 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20po?= =?UTF-8?q?st-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b333bc4378d..34b3fd6eee3 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ If you are using Maven without the BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies: ```Groovy -implementation platform('com.google.cloud:libraries-bom:26.22.0') +implementation platform('com.google.cloud:libraries-bom:26.23.0') implementation 'com.google.cloud:google-cloud-spanner' ``` From 3d9d3129c47491cb54dcd319a087109552a170b7 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 15 Sep 2023 13:34:34 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20po?= =?UTF-8?q?st-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b333bc4378d..34b3fd6eee3 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ If you are using Maven without the BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies: ```Groovy -implementation platform('com.google.cloud:libraries-bom:26.22.0') +implementation platform('com.google.cloud:libraries-bom:26.23.0') implementation 'com.google.cloud:google-cloud-spanner' ```