From 7b9d1db28b7037d6b18df88f00b9213f2f6dab80 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Sun, 25 Oct 2020 20:56:06 -0700 Subject: [PATCH 01/18] fix: fixes the code of conduct document (#541) * Update publish_javadoc.sh We don't want quite as much and need to be in `target/devsite/reference`. Source-Author: Les Vogel Source-Date: Thu Oct 22 14:10:05 2020 -0700 Source-Repo: googleapis/synthtool Source-Sha: 9593c3b5b714cc9b17c445aee8834ac2b4b9348b Source-Link: https://github.com/googleapis/synthtool/commit/9593c3b5b714cc9b17c445aee8834ac2b4b9348b * chore(docs): update code of conduct of synthtool and templates Source-Author: Christopher Wilcox Source-Date: Thu Oct 22 14:22:01 2020 -0700 Source-Repo: googleapis/synthtool Source-Sha: 5f6ef0ec5501d33c4667885b37a7685a30d41a76 Source-Link: https://github.com/googleapis/synthtool/commit/5f6ef0ec5501d33c4667885b37a7685a30d41a76 --- .kokoro/release/publish_javadoc.sh | 2 +- CODE_OF_CONDUCT.md | 7 ++++--- synth.metadata | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.kokoro/release/publish_javadoc.sh b/.kokoro/release/publish_javadoc.sh index fbd48654eeb..0219a65374c 100755 --- a/.kokoro/release/publish_javadoc.sh +++ b/.kokoro/release/publish_javadoc.sh @@ -62,7 +62,7 @@ popd # V2 mvn clean site -B -q -Ddevsite.template="${KOKORO_GFILE_DIR}/java/" -pushd target/devsite +pushd target/devsite/reference # create metadata python3 -m docuploader create-metadata \ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 6b2238bb75e..2add2547a81 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,3 +1,4 @@ + # Code of Conduct ## Our Pledge @@ -69,12 +70,12 @@ dispute. If you are unable to resolve the matter for any reason, or if the behavior is threatening or harassing, report it. We are dedicated to providing an environment where participants feel welcome and safe. -Reports should be directed to *[PROJECT STEWARD NAME(s) AND EMAIL(s)]*, the -Project Steward(s) for *[PROJECT NAME]*. It is the Project Steward’s duty to +Reports should be directed to *googleapis-stewards@google.com*, the +Project Steward(s) for *Google Cloud Client Libraries*. It is the Project Steward’s duty to receive and address reported violations of the code of conduct. They will then work with a committee consisting of representatives from the Open Source Programs Office and the Google Open Source Strategy team. If for any reason you -are uncomfortable reaching out the Project Steward, please email +are uncomfortable reaching out to the Project Steward, please email opensource@google.com. We will investigate every complaint, but you may not receive a direct response. diff --git a/synth.metadata b/synth.metadata index d1be22db27e..fd44f97d681 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "768c19dc1b9985f7823ec1e4ca92491936062f3b" + "sha": "5322c951ffaac38bc89c93ab510f312412fe2c53" } }, { @@ -19,7 +19,7 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "6abb59097be84599a1d6091fe534a49e5c5cf948" + "sha": "5f6ef0ec5501d33c4667885b37a7685a30d41a76" } } ], From 39b37548000e1a6617eeb75f107fc31b98df56bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Mon, 26 Oct 2020 04:59:12 +0100 Subject: [PATCH 02/18] test: add tests for session not found retries for async runner (#545) --- .../com/google/cloud/spanner/SessionPool.java | 60 +++-- .../RetryOnInvalidatedSessionTest.java | 248 +++++++++++++++++- 2 files changed, 276 insertions(+), 32 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java index dbd82bbeb4e..be96f651acd 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java @@ -725,14 +725,9 @@ public void close() { @Override public TransactionContext begin() { this.delegate = session.get().transactionManager(); - while (true) { - try { - return internalBegin(); - } catch (SessionNotFoundException e) { - session = sessionPool.replaceSession(e, session); - delegate = session.get().delegate.transactionManager(); - } - } + // This cannot throw a SessionNotFoundException, as it does not call the BeginTransaction RPC. + // Instead, the BeginTransaction will be included with the first statement of the transaction. + return internalBegin(); } private TransactionContext internalBegin() { @@ -743,7 +738,8 @@ private TransactionContext internalBegin() { private SpannerException handleSessionNotFound(SessionNotFoundException notFound) { session = sessionPool.replaceSession(notFound, session); - delegate = session.get().delegate.transactionManager(); + PooledSession pooledSession = session.get(); + delegate = pooledSession.delegate.transactionManager(); restartedAfterSessionNotFound = true; return SpannerExceptionFactory.newSpannerException( ErrorCode.ABORTED, notFound.getMessage(), notFound); @@ -784,7 +780,8 @@ public TransactionContext resetForRetry() { } } catch (SessionNotFoundException e) { session = sessionPool.replaceSession(e, session); - delegate = session.get().delegate.transactionManager(); + PooledSession pooledSession = session.get(); + delegate = pooledSession.delegate.transactionManager(); restartedAfterSessionNotFound = true; } } @@ -852,7 +849,8 @@ public T run(TransactionCallable callable) { break; } catch (SessionNotFoundException e) { session = sessionPool.replaceSession(e, session); - runner = session.get().delegate.readWriteTransaction(); + PooledSession ps = session.get(); + runner = ps.delegate.readWriteTransaction(); } } session.get().markUsed(); @@ -893,24 +891,34 @@ public ApiFuture runAsync(final AsyncWork work, Executor executor) { new Runnable() { @Override public void run() { - SpannerException se = null; + SpannerException exception = null; R r = null; AsyncRunner runner = null; while (true) { + SpannerException se = null; try { runner = session.get().runAsync(); r = runner.runAsync(work, MoreExecutors.directExecutor()).get(); break; } catch (ExecutionException e) { - se = SpannerExceptionFactory.newSpannerException(e.getCause()); + se = SpannerExceptionFactory.asSpannerException(e.getCause()); } catch (InterruptedException e) { se = SpannerExceptionFactory.propagateInterrupt(e); } catch (Throwable t) { se = SpannerExceptionFactory.newSpannerException(t); } finally { - if (se != null && se instanceof SessionNotFoundException) { - session = sessionPool.replaceSession((SessionNotFoundException) se, session); + if (se instanceof SessionNotFoundException) { + try { + // The replaceSession method will re-throw the SessionNotFoundException if the + // session cannot be replaced with a new one. + session = sessionPool.replaceSession((SessionNotFoundException) se, session); + se = null; + } catch (SessionNotFoundException e) { + exception = e; + break; + } } else { + exception = se; break; } } @@ -918,8 +926,8 @@ public void run() { session.get().markUsed(); session.close(); setCommitTimestamp(runner); - if (se != null) { - res.setException(se); + if (exception != null) { + res.setException(exception); } else { res.set(r); } @@ -1023,7 +1031,8 @@ public ReadContext singleUse() { new Function() { @Override public ReadContext apply(PooledSessionFuture session) { - return session.get().delegate.singleUse(); + PooledSession ps = session.get(); + return ps.delegate.singleUse(); } }, SessionPool.this, @@ -1042,7 +1051,8 @@ public ReadContext singleUse(final TimestampBound bound) { new Function() { @Override public ReadContext apply(PooledSessionFuture session) { - return session.get().delegate.singleUse(bound); + PooledSession ps = session.get(); + return ps.delegate.singleUse(bound); } }, SessionPool.this, @@ -1060,7 +1070,8 @@ public ReadOnlyTransaction singleUseReadOnlyTransaction() { new Function() { @Override public ReadOnlyTransaction apply(PooledSessionFuture session) { - return session.get().delegate.singleUseReadOnlyTransaction(); + PooledSession ps = session.get(); + return ps.delegate.singleUseReadOnlyTransaction(); } }, true); @@ -1072,7 +1083,8 @@ public ReadOnlyTransaction singleUseReadOnlyTransaction(final TimestampBound bou new Function() { @Override public ReadOnlyTransaction apply(PooledSessionFuture session) { - return session.get().delegate.singleUseReadOnlyTransaction(bound); + PooledSession ps = session.get(); + return ps.delegate.singleUseReadOnlyTransaction(bound); } }, true); @@ -1084,7 +1096,8 @@ public ReadOnlyTransaction readOnlyTransaction() { new Function() { @Override public ReadOnlyTransaction apply(PooledSessionFuture session) { - return session.get().delegate.readOnlyTransaction(); + PooledSession ps = session.get(); + return ps.delegate.readOnlyTransaction(); } }, false); @@ -1096,7 +1109,8 @@ public ReadOnlyTransaction readOnlyTransaction(final TimestampBound bound) { new Function() { @Override public ReadOnlyTransaction apply(PooledSessionFuture session) { - return session.get().delegate.readOnlyTransaction(bound); + PooledSession ps = session.get(); + return ps.delegate.readOnlyTransaction(bound); } }, false); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java index 5e732c1eabb..4c7f6d26c96 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java @@ -16,14 +16,20 @@ package com.google.cloud.spanner; +import static com.google.cloud.spanner.SpannerApiFutures.get; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; +import com.google.api.core.ApiFunction; import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; import com.google.api.gax.core.NoCredentialsProvider; import com.google.api.gax.grpc.testing.LocalChannelProvider; import com.google.cloud.NoCredentials; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.AsyncResultSet.CallbackResponse; +import com.google.cloud.spanner.AsyncResultSet.ReadyCallback; +import com.google.cloud.spanner.AsyncRunner.AsyncWork; import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; import com.google.cloud.spanner.TransactionRunner.TransactionCallable; import com.google.cloud.spanner.v1.SpannerClient; @@ -31,6 +37,7 @@ import com.google.cloud.spanner.v1.SpannerSettings; import com.google.common.base.Function; import com.google.common.base.Stopwatch; +import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.ListValue; import com.google.spanner.v1.ResultSetMetadata; import com.google.spanner.v1.StructType; @@ -47,6 +54,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -144,7 +152,6 @@ public static Collection data() { private static final Statement UPDATE_STATEMENT = Statement.of("UPDATE FOO SET BAR=1 WHERE BAZ=2"); private static final long UPDATE_COUNT = 1L; - private static final float WRITE_SESSIONS_FRACTION = 0.5f; private static MockSpannerServiceImpl mockSpanner; private static Server server; private static LocalChannelProvider channelProvider; @@ -194,10 +201,7 @@ public static void stopServer() throws InterruptedException { @Before public void setUp() { mockSpanner.reset(); - SessionPoolOptions.Builder builder = - SessionPoolOptions.newBuilder() - .setWriteSessionsFraction(WRITE_SESSIONS_FRACTION) - .setFailOnSessionLeak(); + SessionPoolOptions.Builder builder = SessionPoolOptions.newBuilder().setFailOnSessionLeak(); if (failOnInvalidatedSession) { builder.setFailIfSessionNotFound(); } @@ -567,7 +571,6 @@ public void readOnlyTransactionReadRowUsingIndexNonRecoverable() throws Interrup @Test public void readWriteTransactionReadOnlySessionInPool() throws InterruptedException { - // Create a session pool with only read sessions. SessionPoolOptions.Builder builder = SessionPoolOptions.newBuilder(); if (failOnInvalidatedSession) { builder.setFailIfSessionNotFound(); @@ -968,9 +971,7 @@ public Integer run(TransactionContext transaction) throws Exception { @SuppressWarnings("resource") @Test public void transactionManagerReadOnlySessionInPool() throws InterruptedException { - // Create a session pool with only read sessions. - SessionPoolOptions.Builder builder = - SessionPoolOptions.newBuilder().setWriteSessionsFraction(0.0f); + SessionPoolOptions.Builder builder = SessionPoolOptions.newBuilder(); if (failOnInvalidatedSession) { builder.setFailIfSessionNotFound(); } @@ -1166,6 +1167,41 @@ public void transactionManagerUpdate() throws InterruptedException { } } + @SuppressWarnings("resource") + @Test + public void transactionManagerAborted_thenSessionNotFoundOnBeginTransaction() + throws InterruptedException { + int attempt = 0; + try (TransactionManager manager = client.transactionManager()) { + long count; + TransactionContext transaction = manager.begin(); + while (true) { + try { + attempt++; + if (attempt == 1) { + mockSpanner.abortNextStatement(); + } + if (attempt == 2) { + invalidateSessionPool(); + } + count = transaction.executeUpdate(UPDATE_STATEMENT); + manager.commit(); + break; + } catch (AbortedException e) { + Thread.sleep(e.getRetryDelayInMillis() / 1000); + transaction = manager.resetForRetry(); + } + } + assertThat(count).isEqualTo(UPDATE_COUNT); + assertThat(failOnInvalidatedSession).isFalse(); + // The actual number of attempts depends on when the transaction manager will actually get a + // valid session, as we invalidate the entire session pool. + assertThat(attempt).isAtLeast(3); + } catch (SessionNotFoundException e) { + assertThat(failOnInvalidatedSession).isTrue(); + } + } + @SuppressWarnings("resource") @Test public void transactionManagerBatchUpdate() throws InterruptedException { @@ -1425,4 +1461,198 @@ public void writeAtLeastOnce() throws InterruptedException { assertThat(failOnInvalidatedSession).isTrue(); } } + + @Test + public void asyncRunnerSelect() throws InterruptedException { + asyncRunner_withReadFunction( + new Function() { + @Override + public AsyncResultSet apply(TransactionContext input) { + return input.executeQueryAsync(SELECT1AND2); + } + }); + } + + @Test + public void asyncRunnerRead() throws InterruptedException { + asyncRunner_withReadFunction( + new Function() { + @Override + public AsyncResultSet apply(TransactionContext input) { + return input.readAsync("FOO", KeySet.all(), Arrays.asList("BAR")); + } + }); + } + + @Test + public void asyncRunnerReadUsingIndex() throws InterruptedException { + asyncRunner_withReadFunction( + new Function() { + @Override + public AsyncResultSet apply(TransactionContext input) { + return input.readUsingIndexAsync("FOO", "IDX", KeySet.all(), Arrays.asList("BAR")); + } + }); + } + + private void asyncRunner_withReadFunction( + final Function readFunction) throws InterruptedException { + invalidateSessionPool(); + final ExecutorService queryExecutor = Executors.newSingleThreadExecutor(); + try { + AsyncRunner runner = client.runAsync(); + final AtomicLong counter = new AtomicLong(); + ApiFuture count = + runner.runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + AsyncResultSet rs = readFunction.apply(txn); + ApiFuture fut = + rs.setCallback( + queryExecutor, + new ReadyCallback() { + @Override + public CallbackResponse cursorReady(AsyncResultSet resultSet) { + while (true) { + switch (resultSet.tryNext()) { + case OK: + counter.incrementAndGet(); + break; + case DONE: + return CallbackResponse.DONE; + case NOT_READY: + return CallbackResponse.CONTINUE; + } + } + } + }); + return ApiFutures.transform( + fut, + new ApiFunction() { + @Override + public Long apply(Void input) { + return counter.get(); + } + }, + MoreExecutors.directExecutor()); + } + }, + executor); + assertThat(get(count)).isEqualTo(2); + assertThat(failOnInvalidatedSession).isFalse(); + } catch (SessionNotFoundException e) { + assertThat(failOnInvalidatedSession).isTrue(); + } finally { + queryExecutor.shutdown(); + } + } + + @Test + public void asyncRunnerReadRow() throws InterruptedException { + invalidateSessionPool(); + try { + AsyncRunner runner = client.runAsync(); + ApiFuture row = + runner.runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + return txn.readRowAsync("FOO", Key.of(), Arrays.asList("BAR")); + } + }, + executor); + assertThat(get(row).getLong(0)).isEqualTo(1L); + assertThat(failOnInvalidatedSession).isFalse(); + } catch (SessionNotFoundException e) { + assertThat(failOnInvalidatedSession).isTrue(); + } + } + + @Test + public void asyncRunnerReadRowUsingIndex() throws InterruptedException { + invalidateSessionPool(); + try { + AsyncRunner runner = client.runAsync(); + ApiFuture row = + runner.runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + return txn.readRowUsingIndexAsync("FOO", "IDX", Key.of(), Arrays.asList("BAR")); + } + }, + executor); + assertThat(get(row).getLong(0)).isEqualTo(1L); + assertThat(failOnInvalidatedSession).isFalse(); + } catch (SessionNotFoundException e) { + assertThat(failOnInvalidatedSession).isTrue(); + } + } + + @Test + public void asyncRunnerUpdate() throws InterruptedException { + invalidateSessionPool(); + try { + AsyncRunner runner = client.runAsync(); + ApiFuture count = + runner.runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + return txn.executeUpdateAsync(UPDATE_STATEMENT); + } + }, + executor); + assertThat(get(count)).isEqualTo(UPDATE_COUNT); + assertThat(failOnInvalidatedSession).isFalse(); + } catch (SessionNotFoundException e) { + assertThat(failOnInvalidatedSession).isTrue(); + } + } + + @Test + public void asyncRunnerBatchUpdate() throws InterruptedException { + invalidateSessionPool(); + try { + AsyncRunner runner = client.runAsync(); + ApiFuture count = + runner.runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + return txn.batchUpdateAsync(Arrays.asList(UPDATE_STATEMENT, UPDATE_STATEMENT)); + } + }, + executor); + assertThat(get(count)).hasLength(2); + assertThat(get(count)).asList().containsExactly(UPDATE_COUNT, UPDATE_COUNT); + assertThat(failOnInvalidatedSession).isFalse(); + } catch (SessionNotFoundException e) { + assertThat(failOnInvalidatedSession).isTrue(); + } + } + + @Test + public void asyncRunnerBuffer() throws InterruptedException { + invalidateSessionPool(); + try { + AsyncRunner runner = client.runAsync(); + ApiFuture res = + runner.runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + txn.buffer(Mutation.newInsertBuilder("FOO").set("BAR").to(1L).build()); + return ApiFutures.immediateFuture(null); + } + }, + executor); + assertThat(get(res)).isNull(); + assertThat(get(runner.getCommitTimestamp())).isNotNull(); + assertThat(failOnInvalidatedSession).isFalse(); + } catch (SessionNotFoundException e) { + assertThat(failOnInvalidatedSession).isTrue(); + } + } } From 729dbf0644c7906e144460de44ac3d68cbae79c7 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Mon, 26 Oct 2020 06:47:42 +0100 Subject: [PATCH 03/18] chore(deps): update dependency com.google.cloud:libraries-bom to v13.2.0 (#543) --- samples/snippets/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 205232a160a..cb0386078ea 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -31,7 +31,7 @@ com.google.cloud libraries-bom - 13.1.0 + 13.2.0 pom import From d1a8824ec4763497be3e32b324461f0379365893 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Sun, 25 Oct 2020 23:20:02 -0700 Subject: [PATCH 04/18] chore: regenerate README (#547) This PR was generated using Autosynth. :rainbow:
Log from Synthtool ``` 2020-10-26 05:49:48,477 synthtool [DEBUG] > Executing /root/.cache/synthtool/java-spanner/.github/readme/synth.py. On branch autosynth-readme nothing to commit, working directory clean 2020-10-26 05:49:49,435 synthtool [DEBUG] > Wrote metadata to .github/readme/synth.metadata/synth.metadata. ```
Full log will be available here: https://source.cloud.google.com/results/invocations/3dbbe195-c18e-4967-9d42-6f0a858b5720/targets - [ ] To automatically regenerate this PR, check this box. --- .github/readme/synth.metadata/synth.metadata | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/readme/synth.metadata/synth.metadata b/.github/readme/synth.metadata/synth.metadata index 6676f1f406e..ab9376d2454 100644 --- a/.github/readme/synth.metadata/synth.metadata +++ b/.github/readme/synth.metadata/synth.metadata @@ -4,14 +4,14 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "35345acdd902b5c7be5d073349d6def6748eaf32" + "sha": "729dbf0644c7906e144460de44ac3d68cbae79c7" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "901ddd44e9ef7887ee681b9183bbdea99437fdcc" + "sha": "a783321fd55f010709294455584a553f4b24b944" } } ] diff --git a/README.md b/README.md index 23a232752ca..768361bb4d5 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ If you are using Maven with [BOM][libraries-bom], add this to your pom.xml file com.google.cloud libraries-bom - 13.1.0 + 13.2.0 pom import From edb7ef41c599b71f3b57e429189f70db5f72b77b Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2020 08:38:03 +0000 Subject: [PATCH 05/18] chore: release 3.0.1-SNAPSHOT (#548) :robot: I have created a release \*beep\* \*boop\* --- ### Updating meta-information for bleeding-edge SNAPSHOT release. --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). --- google-cloud-spanner-bom/pom.xml | 18 +++++++++--------- google-cloud-spanner/pom.xml | 4 ++-- .../pom.xml | 4 ++-- .../pom.xml | 4 ++-- grpc-google-cloud-spanner-v1/pom.xml | 4 ++-- pom.xml | 16 ++++++++-------- .../pom.xml | 4 ++-- .../pom.xml | 4 ++-- proto-google-cloud-spanner-v1/pom.xml | 4 ++-- samples/snapshot/pom.xml | 2 +- versions.txt | 14 +++++++------- 11 files changed, 39 insertions(+), 39 deletions(-) diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index 6ac4096f612..ccb55576bd9 100644 --- a/google-cloud-spanner-bom/pom.xml +++ b/google-cloud-spanner-bom/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-spanner-bom - 3.0.0 + 3.0.1-SNAPSHOT pom com.google.cloud @@ -64,43 +64,43 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 3.0.0 + 3.0.1-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-v1 - 3.0.0 + 3.0.1-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-v1 - 3.0.0 + 3.0.1-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 3.0.0 + 3.0.1-SNAPSHOT com.google.cloud google-cloud-spanner - 3.0.0 + 3.0.1-SNAPSHOT com.google.cloud google-cloud-spanner test-jar - 3.0.0 + 3.0.1-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 3.0.0 + 3.0.1-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 3.0.0 + 3.0.1-SNAPSHOT diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index dd25cc4282b..9fd9cbdf930 100644 --- a/google-cloud-spanner/pom.xml +++ b/google-cloud-spanner/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-spanner - 3.0.0 + 3.0.1-SNAPSHOT jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 3.0.0 + 3.0.1-SNAPSHOT google-cloud-spanner diff --git a/grpc-google-cloud-spanner-admin-database-v1/pom.xml b/grpc-google-cloud-spanner-admin-database-v1/pom.xml index d01643b8c5b..8cc769e0f5a 100644 --- a/grpc-google-cloud-spanner-admin-database-v1/pom.xml +++ b/grpc-google-cloud-spanner-admin-database-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 3.0.0 + 3.0.1-SNAPSHOT grpc-google-cloud-spanner-admin-database-v1 GRPC library for grpc-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 3.0.0 + 3.0.1-SNAPSHOT diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml index 0e9f56798a2..63ab087f58f 100644 --- a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml +++ b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 3.0.0 + 3.0.1-SNAPSHOT grpc-google-cloud-spanner-admin-instance-v1 GRPC library for grpc-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 3.0.0 + 3.0.1-SNAPSHOT diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml index 11aed44c564..ce6f71ae027 100644 --- a/grpc-google-cloud-spanner-v1/pom.xml +++ b/grpc-google-cloud-spanner-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 3.0.0 + 3.0.1-SNAPSHOT grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 3.0.0 + 3.0.1-SNAPSHOT diff --git a/pom.xml b/pom.xml index 143510d62b3..2ba1e541c4f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-spanner-parent pom - 3.0.0 + 3.0.1-SNAPSHOT Google Cloud Spanner Parent https://github.com/googleapis/java-spanner @@ -71,37 +71,37 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 3.0.0 + 3.0.1-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-v1 - 3.0.0 + 3.0.1-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 3.0.0 + 3.0.1-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-v1 - 3.0.0 + 3.0.1-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 3.0.0 + 3.0.1-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 3.0.0 + 3.0.1-SNAPSHOT com.google.cloud google-cloud-spanner - 3.0.0 + 3.0.1-SNAPSHOT diff --git a/proto-google-cloud-spanner-admin-database-v1/pom.xml b/proto-google-cloud-spanner-admin-database-v1/pom.xml index 7646276dd3d..b256685bb67 100644 --- a/proto-google-cloud-spanner-admin-database-v1/pom.xml +++ b/proto-google-cloud-spanner-admin-database-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 3.0.0 + 3.0.1-SNAPSHOT proto-google-cloud-spanner-admin-database-v1 PROTO library for proto-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 3.0.0 + 3.0.1-SNAPSHOT diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml index 886bc032683..dcad0353ff6 100644 --- a/proto-google-cloud-spanner-admin-instance-v1/pom.xml +++ b/proto-google-cloud-spanner-admin-instance-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 3.0.0 + 3.0.1-SNAPSHOT proto-google-cloud-spanner-admin-instance-v1 PROTO library for proto-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 3.0.0 + 3.0.1-SNAPSHOT diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml index 9877d4f86d8..958b9821865 100644 --- a/proto-google-cloud-spanner-v1/pom.xml +++ b/proto-google-cloud-spanner-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 3.0.0 + 3.0.1-SNAPSHOT proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 3.0.0 + 3.0.1-SNAPSHOT diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 071d5830d3a..9b23c6d2d0a 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -31,7 +31,7 @@ com.google.cloud google-cloud-spanner - 3.0.0 + 3.0.1-SNAPSHOT diff --git a/versions.txt b/versions.txt index 2ef2dd9f99d..8ed8d5a3979 100644 --- a/versions.txt +++ b/versions.txt @@ -1,10 +1,10 @@ # Format: # module:released-version:current-version -proto-google-cloud-spanner-admin-instance-v1:3.0.0:3.0.0 -proto-google-cloud-spanner-v1:3.0.0:3.0.0 -proto-google-cloud-spanner-admin-database-v1:3.0.0:3.0.0 -grpc-google-cloud-spanner-v1:3.0.0:3.0.0 -grpc-google-cloud-spanner-admin-instance-v1:3.0.0:3.0.0 -grpc-google-cloud-spanner-admin-database-v1:3.0.0:3.0.0 -google-cloud-spanner:3.0.0:3.0.0 \ No newline at end of file +proto-google-cloud-spanner-admin-instance-v1:3.0.0:3.0.1-SNAPSHOT +proto-google-cloud-spanner-v1:3.0.0:3.0.1-SNAPSHOT +proto-google-cloud-spanner-admin-database-v1:3.0.0:3.0.1-SNAPSHOT +grpc-google-cloud-spanner-v1:3.0.0:3.0.1-SNAPSHOT +grpc-google-cloud-spanner-admin-instance-v1:3.0.0:3.0.1-SNAPSHOT +grpc-google-cloud-spanner-admin-database-v1:3.0.0:3.0.1-SNAPSHOT +google-cloud-spanner:3.0.0:3.0.1-SNAPSHOT \ No newline at end of file From d92b7c21b8751d4f3aae2e8847fa6cb5d3ebd40a Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Mon, 26 Oct 2020 02:12:02 -0700 Subject: [PATCH 06/18] chore: regenerate README (#551) This PR was generated using Autosynth. :rainbow:
Log from Synthtool ``` 2020-10-26 08:42:06,605 synthtool [DEBUG] > Executing /root/.cache/synthtool/java-spanner/.github/readme/synth.py. On branch autosynth-readme nothing to commit, working directory clean 2020-10-26 08:42:07,665 synthtool [DEBUG] > Wrote metadata to .github/readme/synth.metadata/synth.metadata. ```
Full log will be available here: https://source.cloud.google.com/results/invocations/057218f0-707f-43a3-adb6-dbbea1e96845/targets - [ ] To automatically regenerate this PR, check this box. --- .github/readme/synth.metadata/synth.metadata | 2 +- README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/readme/synth.metadata/synth.metadata b/.github/readme/synth.metadata/synth.metadata index ab9376d2454..64e34dca5d3 100644 --- a/.github/readme/synth.metadata/synth.metadata +++ b/.github/readme/synth.metadata/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "729dbf0644c7906e144460de44ac3d68cbae79c7" + "sha": "edb7ef41c599b71f3b57e429189f70db5f72b77b" } }, { diff --git a/README.md b/README.md index 768361bb4d5..7aedeb61f64 100644 --- a/README.md +++ b/README.md @@ -45,11 +45,11 @@ If you are using Maven without BOM, add this to your dependencies: If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-spanner:2.0.2' +compile 'com.google.cloud:google-cloud-spanner:3.0.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "2.0.2" +libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "3.0.0" ``` ## Authentication From 1d36415e26b3d0770ea9d1f76d085efcefe75374 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Mon, 26 Oct 2020 16:56:08 -0700 Subject: [PATCH 07/18] chore(ci): fix typo in restrict presubmit samples ITs to only snapshot (#554) This PR was generated using Autosynth. :rainbow: Synth log will be available here: https://source.cloud.google.com/results/invocations/fe68bcb3-d907-42e1-b991-ed69a91c3c34/targets - [x] To automatically regenerate this PR, check this box. Source-Link: https://github.com/googleapis/synthtool/commit/f68649c5f26bcff6817c6d21e90dac0fc71fef8e --- .kokoro/build.sh | 2 +- synth.metadata | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.kokoro/build.sh b/.kokoro/build.sh index 32aa9311715..8280d10ad67 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -78,7 +78,7 @@ samples) if [[ -f ${SAMPLES_DIR}/pom.xml ]] then - pushd {SAMPLES_DIR} + pushd ${SAMPLES_DIR} mvn -B \ -Penable-samples \ -DtrimStackTrace=false \ diff --git a/synth.metadata b/synth.metadata index fd44f97d681..c993554eac4 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "5322c951ffaac38bc89c93ab510f312412fe2c53" + "sha": "d92b7c21b8751d4f3aae2e8847fa6cb5d3ebd40a" } }, { @@ -19,7 +19,7 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "5f6ef0ec5501d33c4667885b37a7685a30d41a76" + "sha": "f68649c5f26bcff6817c6d21e90dac0fc71fef8e" } } ], From 0f4c017f112478ffc7dd15b0b234a9c48cd55a6e Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 27 Oct 2020 00:57:07 +0100 Subject: [PATCH 08/18] deps: update dependency com.google.cloud:google-cloud-shared-dependencies to v0.13.0 (#521) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2ba1e541c4f..eafbf74754a 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ UTF-8 github google-cloud-spanner-parent - 0.10.2 + 0.13.0
From 226c4f52aa252b9655ad39bf7825b490cf6de782 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 27 Oct 2020 00:58:03 +0100 Subject: [PATCH 09/18] chore(deps): update dependency com.google.cloud:libraries-bom to v13.3.0 (#553) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [com.google.cloud:libraries-bom](https://togithub.com/GoogleCloudPlatform/cloud-opensource-java) | minor | `13.2.0` -> `13.3.0` | --- ### Renovate configuration :date: **Schedule**: At any time (no schedule defined). :vertical_traffic_light: **Automerge**: Disabled by config. Please merge this manually once you are satisfied. :recycle: **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. :no_bell: **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-spanner). --- samples/snippets/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index cb0386078ea..11005d8b57b 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -31,7 +31,7 @@ com.google.cloud libraries-bom - 13.2.0 + 13.3.0 pom import From 4aa9d31da9e3773af184b83516ba837360ab3e0f Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 27 Oct 2020 04:45:17 +0100 Subject: [PATCH 10/18] test(deps): update dependency com.google.truth:truth to v1.1 (#529) --- pom.xml | 2 +- samples/install-without-bom/pom.xml | 2 +- samples/snapshot/pom.xml | 2 +- samples/snippets/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index eafbf74754a..da2b5285bd9 100644 --- a/pom.xml +++ b/pom.xml @@ -121,7 +121,7 @@ com.google.truth truth - 1.0.1 + 1.1 test diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index dceea3d1788..5f8b2699e06 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -99,7 +99,7 @@ com.google.truth truth - 1.0.1 + 1.1 test diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 9b23c6d2d0a..9fb9876e762 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -98,7 +98,7 @@ com.google.truth truth - 1.0.1 + 1.1 test diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 11005d8b57b..3bec4e2dad0 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -108,7 +108,7 @@ com.google.truth truth - 1.0.1 + 1.1 test From 03c54d8a42138d256d64bb9a626289a6c361382a Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 27 Oct 2020 04:45:47 +0100 Subject: [PATCH 11/18] chore(deps): update dependency com.google.cloud:google-cloud-spanner to v3 (#549) --- samples/install-without-bom/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index 5f8b2699e06..3c1c9b5d1ce 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -32,7 +32,7 @@ com.google.cloud google-cloud-spanner - 2.0.2 + 3.0.0 From acf52a2df9dc8ccc7f8b01bfebad474471196955 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Mon, 26 Oct 2020 21:12:07 -0700 Subject: [PATCH 12/18] chore: regenerate README (#555) This PR was generated using Autosynth. :rainbow:
Log from Synthtool ``` 2020-10-26 23:59:21,282 synthtool [DEBUG] > Executing /root/.cache/synthtool/java-spanner/.github/readme/synth.py. On branch autosynth-readme nothing to commit, working directory clean 2020-10-26 23:59:22,319 synthtool [DEBUG] > Wrote metadata to .github/readme/synth.metadata/synth.metadata. ```
Full log will be available here: https://source.cloud.google.com/results/invocations/d2bdcfe4-2eeb-4ec5-9c45-4c5f2d939447/targets - [ ] To automatically regenerate this PR, check this box. --- .github/readme/synth.metadata/synth.metadata | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/readme/synth.metadata/synth.metadata b/.github/readme/synth.metadata/synth.metadata index 64e34dca5d3..93e513fffaa 100644 --- a/.github/readme/synth.metadata/synth.metadata +++ b/.github/readme/synth.metadata/synth.metadata @@ -4,14 +4,14 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "edb7ef41c599b71f3b57e429189f70db5f72b77b" + "sha": "226c4f52aa252b9655ad39bf7825b490cf6de782" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "a783321fd55f010709294455584a553f4b24b944" + "sha": "fd3584b01cedd6c9f79d08be4e2365085f955aa5" } } ] diff --git a/README.md b/README.md index 7aedeb61f64..cad5241bab5 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ If you are using Maven with [BOM][libraries-bom], add this to your pom.xml file com.google.cloud libraries-bom - 13.2.0 + 13.3.0 pom import From 5969f8313a4df6ece63ee8f14df98cbc8511f026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Tue, 27 Oct 2020 05:58:38 +0100 Subject: [PATCH 13/18] fix: SessionNotFound was not retried for AsyncTransactionManager (#552) --- .../spanner/AsyncTransactionManagerImpl.java | 6 +- .../com/google/cloud/spanner/SessionPool.java | 359 +++++++++++------- .../SessionPoolAsyncTransactionManager.java | 60 ++- .../spanner/AsyncTransactionManagerTest.java | 5 +- .../cloud/spanner/MockSpannerServiceImpl.java | 4 + .../RetryOnInvalidatedSessionTest.java | 332 ++++++++++++++++ 6 files changed, 608 insertions(+), 158 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java index 2ba66d0c864..7dda2601021 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java @@ -66,7 +66,9 @@ public ApiFuture closeAsync() { if (txnState == TransactionState.STARTED) { res = rollbackAsync(); } - txn.close(); + if (txn != null) { + txn.close(); + } return MoreObjects.firstNonNull(res, ApiFutures.immediateFuture(null)); } @@ -172,7 +174,7 @@ public ApiFuture apply(Empty input) throws Exception { @Override public TransactionContextFuture resetForRetryAsync() { - if (txn == null || !txn.isAborted() && txnState != TransactionState.ABORTED) { + if (txn == null || (!txn.isAborted() && txnState != TransactionState.ABORTED)) { throw new IllegalStateException( "resetForRetry can only be called if the previous attempt aborted"); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java index be96f651acd..f2f8601516c 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java @@ -38,6 +38,7 @@ import static com.google.cloud.spanner.MetricRegistryConstants.SPANNER_LABEL_KEYS_WITH_TYPE; import static com.google.cloud.spanner.SpannerExceptionFactory.newSpannerException; +import com.google.api.core.ApiFunction; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; import com.google.api.core.SettableApiFuture; @@ -540,177 +541,248 @@ public Timestamp getReadTimestamp() { } } - private static class AutoClosingTransactionManager implements TransactionManager { - private class SessionPoolResultSet extends ForwardingResultSet { - private SessionPoolResultSet(ResultSet delegate) { - super(delegate); - } + interface SessionNotFoundHandler { + /** + * Handles the given {@link SessionNotFoundException} by possibly converting it to a different + * exception that should be thrown. + */ + SpannerException handleSessionNotFound(SessionNotFoundException notFound); + } - @Override - public boolean next() { - try { - return super.next(); - } catch (SessionNotFoundException e) { - throw handleSessionNotFound(e); - } + static class SessionPoolResultSet extends ForwardingResultSet { + private final SessionNotFoundHandler handler; + + private SessionPoolResultSet(SessionNotFoundHandler handler, ResultSet delegate) { + super(delegate); + this.handler = Preconditions.checkNotNull(handler); + } + + @Override + public boolean next() { + try { + return super.next(); + } catch (SessionNotFoundException e) { + throw handler.handleSessionNotFound(e); } } + } - /** - * {@link TransactionContext} that is used in combination with an {@link - * AutoClosingTransactionManager}. This {@link TransactionContext} handles {@link - * SessionNotFoundException}s by replacing the underlying session with a fresh one, and then - * throws an {@link AbortedException} to trigger the retry-loop that has been created by the - * caller. - */ - private class SessionPoolTransactionContext implements TransactionContext { - private final TransactionContext delegate; + static class AsyncSessionPoolResultSet extends ForwardingAsyncResultSet { + private final SessionNotFoundHandler handler; - private SessionPoolTransactionContext(TransactionContext delegate) { - this.delegate = delegate; - } + private AsyncSessionPoolResultSet(SessionNotFoundHandler handler, AsyncResultSet delegate) { + super(delegate); + this.handler = Preconditions.checkNotNull(handler); + } - @Override - public ResultSet read( - String table, KeySet keys, Iterable columns, ReadOption... options) { - return new SessionPoolResultSet(delegate.read(table, keys, columns, options)); - } + @Override + public ApiFuture setCallback(Executor executor, final ReadyCallback callback) { + return super.setCallback( + executor, + new ReadyCallback() { + @Override + public CallbackResponse cursorReady(AsyncResultSet resultSet) { + try { + return callback.cursorReady(resultSet); + } catch (SessionNotFoundException e) { + throw handler.handleSessionNotFound(e); + } + } + }); + } - @Override - public AsyncResultSet readAsync( - String table, KeySet keys, Iterable columns, ReadOption... options) { - throw SpannerExceptionFactory.newSpannerException( - ErrorCode.UNIMPLEMENTED, "not yet implemented"); + @Override + public boolean next() { + try { + return super.next(); + } catch (SessionNotFoundException e) { + throw handler.handleSessionNotFound(e); } + } - @Override - public ResultSet readUsingIndex( - String table, - String index, - KeySet keys, - Iterable columns, - ReadOption... options) { - return new SessionPoolResultSet( - delegate.readUsingIndex(table, index, keys, columns, options)); + @Override + public CursorState tryNext() { + try { + return super.tryNext(); + } catch (SessionNotFoundException e) { + throw handler.handleSessionNotFound(e); } + } + } - @Override - public AsyncResultSet readUsingIndexAsync( - String table, - String index, - KeySet keys, - Iterable columns, - ReadOption... options) { - throw SpannerExceptionFactory.newSpannerException( - ErrorCode.UNIMPLEMENTED, "not yet implemented"); - } + /** + * {@link TransactionContext} that is used in combination with an {@link + * AutoClosingTransactionManager}. This {@link TransactionContext} handles {@link + * SessionNotFoundException}s by replacing the underlying session with a fresh one, and then + * throws an {@link AbortedException} to trigger the retry-loop that has been created by the + * caller. + */ + static class SessionPoolTransactionContext implements TransactionContext { + private final SessionNotFoundHandler handler; + final TransactionContext delegate; - @Override - public Struct readRow(String table, Key key, Iterable columns) { - try { - return delegate.readRow(table, key, columns); - } catch (SessionNotFoundException e) { - throw handleSessionNotFound(e); - } - } + SessionPoolTransactionContext(SessionNotFoundHandler handler, TransactionContext delegate) { + this.handler = Preconditions.checkNotNull(handler); + this.delegate = delegate; + } - @Override - public ApiFuture readRowAsync(String table, Key key, Iterable columns) { - try (AsyncResultSet rs = readAsync(table, KeySet.singleKey(key), columns)) { - return AbstractReadContext.consumeSingleRowAsync(rs); - } - } + @Override + public ResultSet read( + String table, KeySet keys, Iterable columns, ReadOption... options) { + return new SessionPoolResultSet(handler, delegate.read(table, keys, columns, options)); + } - @Override - public void buffer(Mutation mutation) { - delegate.buffer(mutation); - } + @Override + public AsyncResultSet readAsync( + String table, KeySet keys, Iterable columns, ReadOption... options) { + return new AsyncSessionPoolResultSet( + handler, delegate.readAsync(table, keys, columns, options)); + } - @Override - public Struct readRowUsingIndex( - String table, String index, Key key, Iterable columns) { - try { - return delegate.readRowUsingIndex(table, index, key, columns); - } catch (SessionNotFoundException e) { - throw handleSessionNotFound(e); - } - } + @Override + public ResultSet readUsingIndex( + String table, String index, KeySet keys, Iterable columns, ReadOption... options) { + return new SessionPoolResultSet( + handler, delegate.readUsingIndex(table, index, keys, columns, options)); + } - @Override - public ApiFuture readRowUsingIndexAsync( - String table, String index, Key key, Iterable columns) { - try (AsyncResultSet rs = - readUsingIndexAsync(table, index, KeySet.singleKey(key), columns)) { - return AbstractReadContext.consumeSingleRowAsync(rs); - } - } + @Override + public AsyncResultSet readUsingIndexAsync( + String table, String index, KeySet keys, Iterable columns, ReadOption... options) { + return new AsyncSessionPoolResultSet( + handler, delegate.readUsingIndexAsync(table, index, keys, columns, options)); + } - @Override - public void buffer(Iterable mutations) { - delegate.buffer(mutations); + @Override + public Struct readRow(String table, Key key, Iterable columns) { + try { + return delegate.readRow(table, key, columns); + } catch (SessionNotFoundException e) { + throw handler.handleSessionNotFound(e); } + } - @Override - public long executeUpdate(Statement statement) { - try { - return delegate.executeUpdate(statement); - } catch (SessionNotFoundException e) { - throw handleSessionNotFound(e); - } + @Override + public ApiFuture readRowAsync(String table, Key key, Iterable columns) { + try (AsyncResultSet rs = readAsync(table, KeySet.singleKey(key), columns)) { + return ApiFutures.catching( + AbstractReadContext.consumeSingleRowAsync(rs), + SessionNotFoundException.class, + new ApiFunction() { + @Override + public Struct apply(SessionNotFoundException input) { + throw handler.handleSessionNotFound(input); + } + }, + MoreExecutors.directExecutor()); } + } - @Override - public ApiFuture executeUpdateAsync(Statement statement) { - try { - return delegate.executeUpdateAsync(statement); - } catch (SessionNotFoundException e) { - throw handleSessionNotFound(e); - } - } + @Override + public void buffer(Mutation mutation) { + delegate.buffer(mutation); + } - @Override - public long[] batchUpdate(Iterable statements) { - try { - return delegate.batchUpdate(statements); - } catch (SessionNotFoundException e) { - throw handleSessionNotFound(e); - } + @Override + public Struct readRowUsingIndex(String table, String index, Key key, Iterable columns) { + try { + return delegate.readRowUsingIndex(table, index, key, columns); + } catch (SessionNotFoundException e) { + throw handler.handleSessionNotFound(e); } + } - @Override - public ApiFuture batchUpdateAsync(Iterable statements) { - try { - return delegate.batchUpdateAsync(statements); - } catch (SessionNotFoundException e) { - throw handleSessionNotFound(e); - } + @Override + public ApiFuture readRowUsingIndexAsync( + String table, String index, Key key, Iterable columns) { + try (AsyncResultSet rs = readUsingIndexAsync(table, index, KeySet.singleKey(key), columns)) { + return ApiFutures.catching( + AbstractReadContext.consumeSingleRowAsync(rs), + SessionNotFoundException.class, + new ApiFunction() { + @Override + public Struct apply(SessionNotFoundException input) { + throw handler.handleSessionNotFound(input); + } + }, + MoreExecutors.directExecutor()); } + } - @Override - public ResultSet executeQuery(Statement statement, QueryOption... options) { - return new SessionPoolResultSet(delegate.executeQuery(statement, options)); - } + @Override + public void buffer(Iterable mutations) { + delegate.buffer(mutations); + } - @Override - public AsyncResultSet executeQueryAsync(Statement statement, QueryOption... options) { - try { - return delegate.executeQueryAsync(statement, options); - } catch (SessionNotFoundException e) { - throw handleSessionNotFound(e); - } + @Override + public long executeUpdate(Statement statement) { + try { + return delegate.executeUpdate(statement); + } catch (SessionNotFoundException e) { + throw handler.handleSessionNotFound(e); } + } - @Override - public ResultSet analyzeQuery(Statement statement, QueryAnalyzeMode queryMode) { - return new SessionPoolResultSet(delegate.analyzeQuery(statement, queryMode)); - } + @Override + public ApiFuture executeUpdateAsync(Statement statement) { + return ApiFutures.catching( + delegate.executeUpdateAsync(statement), + SessionNotFoundException.class, + new ApiFunction() { + @Override + public Long apply(SessionNotFoundException input) { + throw handler.handleSessionNotFound(input); + } + }, + MoreExecutors.directExecutor()); + } - @Override - public void close() { - delegate.close(); + @Override + public long[] batchUpdate(Iterable statements) { + try { + return delegate.batchUpdate(statements); + } catch (SessionNotFoundException e) { + throw handler.handleSessionNotFound(e); } } + @Override + public ApiFuture batchUpdateAsync(Iterable statements) { + return ApiFutures.catching( + delegate.batchUpdateAsync(statements), + SessionNotFoundException.class, + new ApiFunction() { + @Override + public long[] apply(SessionNotFoundException input) { + throw handler.handleSessionNotFound(input); + } + }, + MoreExecutors.directExecutor()); + } + + @Override + public ResultSet executeQuery(Statement statement, QueryOption... options) { + return new SessionPoolResultSet(handler, delegate.executeQuery(statement, options)); + } + + @Override + public AsyncResultSet executeQueryAsync(Statement statement, QueryOption... options) { + return new AsyncSessionPoolResultSet(handler, delegate.executeQueryAsync(statement, options)); + } + + @Override + public ResultSet analyzeQuery(Statement statement, QueryAnalyzeMode queryMode) { + return new SessionPoolResultSet(handler, delegate.analyzeQuery(statement, queryMode)); + } + + @Override + public void close() { + delegate.close(); + } + } + + private static class AutoClosingTransactionManager + implements TransactionManager, SessionNotFoundHandler { private TransactionManager delegate; private final SessionPool sessionPool; private PooledSessionFuture session; @@ -731,12 +803,13 @@ public TransactionContext begin() { } private TransactionContext internalBegin() { - TransactionContext res = new SessionPoolTransactionContext(delegate.begin()); + TransactionContext res = new SessionPoolTransactionContext(this, delegate.begin()); session.get().markUsed(); return res; } - private SpannerException handleSessionNotFound(SessionNotFoundException notFound) { + @Override + public SpannerException handleSessionNotFound(SessionNotFoundException notFound) { session = sessionPool.replaceSession(notFound, session); PooledSession pooledSession = session.get(); delegate = pooledSession.delegate.transactionManager(); @@ -772,11 +845,11 @@ public TransactionContext resetForRetry() { while (true) { try { if (restartedAfterSessionNotFound) { - TransactionContext res = new SessionPoolTransactionContext(delegate.begin()); + TransactionContext res = new SessionPoolTransactionContext(this, delegate.begin()); restartedAfterSessionNotFound = false; return res; } else { - return new SessionPoolTransactionContext(delegate.resetForRetry()); + return new SessionPoolTransactionContext(this, delegate.resetForRetry()); } } catch (SessionNotFoundException e) { session = sessionPool.replaceSession(e, session); @@ -1145,7 +1218,7 @@ public AsyncRunner runAsync() { @Override public AsyncTransactionManager transactionManagerAsync() { - return new SessionPoolAsyncTransactionManager(this); + return new SessionPoolAsyncTransactionManager(SessionPool.this, this); } @Override diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java index 54b621b93b8..515286fb11d 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java @@ -17,30 +17,40 @@ package com.google.cloud.spanner; import com.google.api.core.ApiAsyncFunction; +import com.google.api.core.ApiFunction; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; import com.google.api.core.SettableApiFuture; import com.google.cloud.Timestamp; import com.google.cloud.spanner.SessionPool.PooledSessionFuture; +import com.google.cloud.spanner.SessionPool.SessionNotFoundHandler; import com.google.cloud.spanner.TransactionContextFutureImpl.CommittableAsyncTransactionManager; import com.google.cloud.spanner.TransactionManager.TransactionState; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.MoreExecutors; import javax.annotation.concurrent.GuardedBy; -class SessionPoolAsyncTransactionManager implements CommittableAsyncTransactionManager { +class SessionPoolAsyncTransactionManager + implements CommittableAsyncTransactionManager, SessionNotFoundHandler { private final Object lock = new Object(); @GuardedBy("lock") private TransactionState txnState; + private final SessionPool pool; private volatile PooledSessionFuture session; - private final SettableApiFuture delegate = - SettableApiFuture.create(); + private volatile SettableApiFuture delegate; + private boolean restartedAfterSessionNotFound; - SessionPoolAsyncTransactionManager(PooledSessionFuture session) { + SessionPoolAsyncTransactionManager(SessionPool pool, PooledSessionFuture session) { + this.pool = Preconditions.checkNotNull(pool); + createTransaction(session); + } + + private void createTransaction(PooledSessionFuture session) { this.session = session; + this.delegate = SettableApiFuture.create(); this.session.addListener( new Runnable() { @Override @@ -56,6 +66,16 @@ public void run() { MoreExecutors.directExecutor()); } + @Override + public SpannerException handleSessionNotFound(SessionNotFoundException notFound) { + // Restart the entire transaction with a new session and throw an AbortedException to force the + // client application to retry. + createTransaction(pool.replaceSession(notFound, session)); + restartedAfterSessionNotFound = true; + return SpannerExceptionFactory.newSpannerException( + ErrorCode.ABORTED, notFound.getMessage(), notFound); + } + @Override public void close() { SpannerApiFutures.get(closeAsync()); @@ -122,7 +142,9 @@ public void onFailure(Throwable t) { @Override public void onSuccess(TransactionContext result) { - delegateTxnFuture.set(result); + delegateTxnFuture.set( + new SessionPool.SessionPoolTransactionContext( + SessionPoolAsyncTransactionManager.this, result)); } }, MoreExecutors.directExecutor()); @@ -215,19 +237,33 @@ public void run() { public TransactionContextFuture resetForRetryAsync() { synchronized (lock) { Preconditions.checkState( - txnState == TransactionState.ABORTED, + txnState == TransactionState.ABORTED || restartedAfterSessionNotFound, "resetForRetry can only be called after the transaction aborted."); txnState = TransactionState.STARTED; } return new TransactionContextFutureImpl( this, - ApiFutures.transformAsync( - delegate, - new ApiAsyncFunction() { + ApiFutures.transform( + ApiFutures.transformAsync( + delegate, + new ApiAsyncFunction() { + @Override + public ApiFuture apply(AsyncTransactionManagerImpl input) + throws Exception { + if (restartedAfterSessionNotFound) { + restartedAfterSessionNotFound = false; + return input.beginAsync(); + } + return input.resetForRetryAsync(); + } + }, + MoreExecutors.directExecutor()), + new ApiFunction() { + @Override - public ApiFuture apply(AsyncTransactionManagerImpl input) - throws Exception { - return input.resetForRetryAsync(); + public TransactionContext apply(TransactionContext input) { + return new SessionPool.SessionPoolTransactionContext( + SessionPoolAsyncTransactionManager.this, input); } }, MoreExecutors.directExecutor())); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java index ddf8f580a50..aed844ed9ad 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java @@ -36,6 +36,7 @@ import com.google.cloud.spanner.MockSpannerServiceImpl.SimulatedExecutionTime; import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; import com.google.cloud.spanner.Options.ReadOption; +import com.google.cloud.spanner.SessionPool.SessionPoolTransactionContext; import com.google.cloud.spanner.TransactionRunnerImpl.TransactionContextImpl; import com.google.common.base.Function; import com.google.common.base.Predicate; @@ -191,7 +192,9 @@ public void asyncTransactionManager_shouldRollbackOnCloseAsync() throws Exceptio AsyncTransactionManager manager = client().transactionManagerAsync(); TransactionContext txn = manager.beginAsync().get(); txn.executeUpdateAsync(UPDATE_STATEMENT).get(); - final TransactionSelector selector = ((TransactionContextImpl) txn).getTransactionSelector(); + final TransactionSelector selector = + ((TransactionContextImpl) ((SessionPoolTransactionContext) txn).delegate) + .getTransactionSelector(); SpannerApiFutures.get(manager.closeAsync()); // The mock server should already have the Rollback request, as we are waiting for the returned diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java index 85e935a75a7..d2715614472 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java @@ -20,6 +20,7 @@ import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.spanner.AbstractResultSet.GrpcStruct; +import com.google.cloud.spanner.SessionPool.SessionPoolTransactionContext; import com.google.cloud.spanner.TransactionRunnerImpl.TransactionContextImpl; import com.google.common.base.Optional; import com.google.common.base.Preconditions; @@ -664,6 +665,9 @@ public void setAbortProbability(double probability) { */ public void abortTransaction(TransactionContext transactionContext) { Preconditions.checkNotNull(transactionContext); + if (transactionContext instanceof SessionPoolTransactionContext) { + transactionContext = ((SessionPoolTransactionContext) transactionContext).delegate; + } if (transactionContext instanceof TransactionContextImpl) { TransactionContextImpl impl = (TransactionContextImpl) transactionContext; ByteString id = diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java index 4c7f6d26c96..e49c7e739cc 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java @@ -30,6 +30,10 @@ import com.google.cloud.spanner.AsyncResultSet.CallbackResponse; import com.google.cloud.spanner.AsyncResultSet.ReadyCallback; import com.google.cloud.spanner.AsyncRunner.AsyncWork; +import com.google.cloud.spanner.AsyncTransactionManager.AsyncTransactionFunction; +import com.google.cloud.spanner.AsyncTransactionManager.AsyncTransactionStep; +import com.google.cloud.spanner.AsyncTransactionManager.CommitTimestampFuture; +import com.google.cloud.spanner.AsyncTransactionManager.TransactionContextFuture; import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; import com.google.cloud.spanner.TransactionRunner.TransactionCallable; import com.google.cloud.spanner.v1.SpannerClient; @@ -1655,4 +1659,332 @@ public ApiFuture doWorkAsync(TransactionContext txn) { assertThat(failOnInvalidatedSession).isTrue(); } } + + @Test + public void asyncTransactionManagerAsyncSelect() throws InterruptedException { + asyncTransactionManager_readAsync( + new Function() { + @Override + public AsyncResultSet apply(TransactionContext input) { + return input.executeQueryAsync(SELECT1AND2); + } + }); + } + + @Test + public void asyncTransactionManagerAsyncRead() throws InterruptedException { + asyncTransactionManager_readAsync( + new Function() { + @Override + public AsyncResultSet apply(TransactionContext input) { + return input.readAsync("FOO", KeySet.all(), Arrays.asList("BAR")); + } + }); + } + + @Test + public void asyncTransactionManagerAsyncReadUsingIndex() throws InterruptedException { + asyncTransactionManager_readAsync( + new Function() { + @Override + public AsyncResultSet apply(TransactionContext input) { + return input.readUsingIndexAsync("FOO", "idx", KeySet.all(), Arrays.asList("BAR")); + } + }); + } + + private void asyncTransactionManager_readAsync( + final Function fn) throws InterruptedException { + invalidateSessionPool(); + final ExecutorService queryExecutor = Executors.newSingleThreadExecutor(); + try (AsyncTransactionManager manager = client.transactionManagerAsync()) { + TransactionContextFuture context = manager.beginAsync(); + while (true) { + try { + final AtomicLong counter = new AtomicLong(); + AsyncTransactionStep count = + context.then( + new AsyncTransactionFunction() { + @Override + public ApiFuture apply(TransactionContext txn, Void input) + throws Exception { + AsyncResultSet rs = fn.apply(txn); + ApiFuture fut = + rs.setCallback( + queryExecutor, + new ReadyCallback() { + @Override + public CallbackResponse cursorReady(AsyncResultSet resultSet) { + while (true) { + switch (resultSet.tryNext()) { + case OK: + counter.incrementAndGet(); + break; + case DONE: + return CallbackResponse.DONE; + case NOT_READY: + return CallbackResponse.CONTINUE; + } + } + } + }); + return ApiFutures.transform( + fut, + new ApiFunction() { + @Override + public Long apply(Void input) { + return counter.get(); + } + }, + MoreExecutors.directExecutor()); + } + }, + executor); + CommitTimestampFuture ts = count.commitAsync(); + assertThat(get(ts)).isNotNull(); + assertThat(get(count)).isEqualTo(2); + assertThat(failOnInvalidatedSession).isFalse(); + break; + } catch (AbortedException e) { + context = manager.resetForRetryAsync(); + } + } + } catch (SessionNotFoundException e) { + assertThat(failOnInvalidatedSession).isTrue(); + } finally { + queryExecutor.shutdown(); + } + } + + @Test + public void asyncTransactionManagerSelect() throws InterruptedException { + asyncTransactionManager_readSync( + new Function() { + @Override + public ResultSet apply(TransactionContext input) { + return input.executeQuery(SELECT1AND2); + } + }); + } + + @Test + public void asyncTransactionManagerRead() throws InterruptedException { + asyncTransactionManager_readSync( + new Function() { + @Override + public ResultSet apply(TransactionContext input) { + return input.read("FOO", KeySet.all(), Arrays.asList("BAR")); + } + }); + } + + @Test + public void asyncTransactionManagerReadUsingIndex() throws InterruptedException { + asyncTransactionManager_readSync( + new Function() { + @Override + public ResultSet apply(TransactionContext input) { + return input.readUsingIndex("FOO", "idx", KeySet.all(), Arrays.asList("BAR")); + } + }); + } + + private void asyncTransactionManager_readSync(final Function fn) + throws InterruptedException { + invalidateSessionPool(); + final ExecutorService queryExecutor = Executors.newSingleThreadExecutor(); + try (AsyncTransactionManager manager = client.transactionManagerAsync()) { + TransactionContextFuture context = manager.beginAsync(); + while (true) { + try { + AsyncTransactionStep count = + context.then( + new AsyncTransactionFunction() { + @Override + public ApiFuture apply(TransactionContext txn, Void input) + throws Exception { + long counter = 0L; + try (ResultSet rs = fn.apply(txn)) { + while (rs.next()) { + counter++; + } + } + return ApiFutures.immediateFuture(counter); + } + }, + executor); + CommitTimestampFuture ts = count.commitAsync(); + assertThat(get(ts)).isNotNull(); + assertThat(get(count)).isEqualTo(2); + assertThat(failOnInvalidatedSession).isFalse(); + break; + } catch (AbortedException e) { + context = manager.resetForRetryAsync(); + } + } + } catch (SessionNotFoundException e) { + assertThat(failOnInvalidatedSession).isTrue(); + } finally { + queryExecutor.shutdown(); + } + } + + @Test + public void asyncTransactionManagerReadRow() throws InterruptedException { + asyncTransactionManager_readRowFunction( + new Function>() { + @Override + public ApiFuture apply(TransactionContext input) { + return ApiFutures.immediateFuture( + input.readRow("FOO", Key.of("foo"), Arrays.asList("BAR"))); + } + }); + } + + @Test + public void asyncTransactionManagerReadRowUsingIndex() throws InterruptedException { + asyncTransactionManager_readRowFunction( + new Function>() { + @Override + public ApiFuture apply(TransactionContext input) { + return ApiFutures.immediateFuture( + input.readRowUsingIndex("FOO", "idx", Key.of("foo"), Arrays.asList("BAR"))); + } + }); + } + + @Test + public void asyncTransactionManagerReadRowAsync() throws InterruptedException { + asyncTransactionManager_readRowFunction( + new Function>() { + @Override + public ApiFuture apply(TransactionContext input) { + return input.readRowAsync("FOO", Key.of("foo"), Arrays.asList("BAR")); + } + }); + } + + @Test + public void asyncTransactionManagerReadRowUsingIndexAsync() throws InterruptedException { + asyncTransactionManager_readRowFunction( + new Function>() { + @Override + public ApiFuture apply(TransactionContext input) { + return input.readRowUsingIndexAsync("FOO", "idx", Key.of("foo"), Arrays.asList("BAR")); + } + }); + } + + private void asyncTransactionManager_readRowFunction( + final Function> fn) throws InterruptedException { + invalidateSessionPool(); + final ExecutorService queryExecutor = Executors.newSingleThreadExecutor(); + try (AsyncTransactionManager manager = client.transactionManagerAsync()) { + TransactionContextFuture context = manager.beginAsync(); + while (true) { + try { + AsyncTransactionStep row = + context.then( + new AsyncTransactionFunction() { + @Override + public ApiFuture apply(TransactionContext txn, Void input) + throws Exception { + return fn.apply(txn); + } + }, + executor); + CommitTimestampFuture ts = row.commitAsync(); + assertThat(get(ts)).isNotNull(); + assertThat(get(row)).isEqualTo(Struct.newBuilder().set("BAR").to(1L).build()); + assertThat(failOnInvalidatedSession).isFalse(); + break; + } catch (AbortedException e) { + context = manager.resetForRetryAsync(); + } + } + } catch (SessionNotFoundException e) { + assertThat(failOnInvalidatedSession).isTrue(); + } finally { + queryExecutor.shutdown(); + } + } + + @Test + public void asyncTransactionManagerUpdateAsync() throws InterruptedException { + asyncTransactionManager_updateFunction( + new Function>() { + @Override + public ApiFuture apply(TransactionContext input) { + return input.executeUpdateAsync(UPDATE_STATEMENT); + } + }, + UPDATE_COUNT); + } + + @Test + public void asyncTransactionManagerUpdate() throws InterruptedException { + asyncTransactionManager_updateFunction( + new Function>() { + @Override + public ApiFuture apply(TransactionContext input) { + return ApiFutures.immediateFuture(input.executeUpdate(UPDATE_STATEMENT)); + } + }, + UPDATE_COUNT); + } + + @Test + public void asyncTransactionManagerBatchUpdateAsync() throws InterruptedException { + asyncTransactionManager_updateFunction( + new Function>() { + @Override + public ApiFuture apply(TransactionContext input) { + return input.batchUpdateAsync(Arrays.asList(UPDATE_STATEMENT, UPDATE_STATEMENT)); + } + }, + new long[] {UPDATE_COUNT, UPDATE_COUNT}); + } + + @Test + public void asyncTransactionManagerBatchUpdate() throws InterruptedException { + asyncTransactionManager_updateFunction( + new Function>() { + @Override + public ApiFuture apply(TransactionContext input) { + return ApiFutures.immediateFuture( + input.batchUpdate(Arrays.asList(UPDATE_STATEMENT, UPDATE_STATEMENT))); + } + }, + new long[] {UPDATE_COUNT, UPDATE_COUNT}); + } + + private void asyncTransactionManager_updateFunction( + final Function> fn, T expected) throws InterruptedException { + invalidateSessionPool(); + try (AsyncTransactionManager manager = client.transactionManagerAsync()) { + TransactionContextFuture transaction = manager.beginAsync(); + while (true) { + try { + AsyncTransactionStep res = + transaction.then( + new AsyncTransactionFunction() { + @Override + public ApiFuture apply(TransactionContext txn, Void input) throws Exception { + return fn.apply(txn); + } + }, + executor); + CommitTimestampFuture ts = res.commitAsync(); + assertThat(get(res)).isEqualTo(expected); + assertThat(get(ts)).isNotNull(); + break; + } catch (AbortedException e) { + transaction = manager.resetForRetryAsync(); + } + } + assertThat(failOnInvalidatedSession).isFalse(); + } catch (SessionNotFoundException e) { + assertThat(failOnInvalidatedSession).isTrue(); + } + } } From b3c96f25d68ebc19b9260cf75afe68b0a39daed4 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 27 Oct 2020 20:34:10 +0100 Subject: [PATCH 14/18] build(deps): update dependency com.google.cloud:google-cloud-shared-config to v0.9.4 (#558) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [com.google.cloud:google-cloud-shared-config](https://togithub.com/googleapis/java-shared-config) | patch | `0.9.3` -> `0.9.4` | --- ### Release Notes
googleapis/java-shared-config ### [`v0.9.4`](https://togithub.com/googleapis/java-shared-config/blob/master/CHANGELOG.md#​094-httpswwwgithubcomgoogleapisjava-shared-configcomparev093v094-2020-10-21) [Compare Source](https://togithub.com/googleapis/java-shared-config/compare/v0.9.3...v0.9.4)
--- ### Renovate configuration :date: **Schedule**: At any time (no schedule defined). :vertical_traffic_light: **Automerge**: Disabled by config. Please merge this manually once you are satisfied. :recycle: **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. :no_bell: **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-spanner). --- google-cloud-spanner-bom/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index ccb55576bd9..b6f76cf64c4 100644 --- a/google-cloud-spanner-bom/pom.xml +++ b/google-cloud-spanner-bom/pom.xml @@ -8,7 +8,7 @@ com.google.cloud google-cloud-shared-config - 0.9.3 + 0.9.4 Google Cloud Spanner BOM diff --git a/pom.xml b/pom.xml index da2b5285bd9..59701286415 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ com.google.cloud google-cloud-shared-config - 0.9.3 + 0.9.4 From ff571b01b9dffdda44a9bd322e04ff04b5b5c57a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Tue, 27 Oct 2020 23:52:01 +0100 Subject: [PATCH 15/18] fix: always delete all backups from an owned test instance (#557) * fix: always delete all backups from an owned test instance An instance can only be deleted once all backups on the instance have been deleted. The integration test environment should therefore first delete all backups on an owned instance before trying to delete the instance itself. Fixes #542 * fix: do not try to list backups on emulator --- .../google/cloud/spanner/IntegrationTestEnv.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java index 687bb44ba15..103b6f626ba 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java @@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState; import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.testing.EmulatorSpannerHelper; import com.google.cloud.spanner.testing.RemoteSpannerHelper; import com.google.common.collect.Iterators; import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; @@ -165,6 +166,20 @@ private void cleanUpInstance() { if (isOwnedInstance) { // Delete the instance, which implicitly drops all databases in it. try { + if (!EmulatorSpannerHelper.isUsingEmulator()) { + // Backups must be explicitly deleted before the instance may be deleted. + logger.log( + Level.FINE, "Deleting backups on test instance {0}", testHelper.getInstanceId()); + for (Backup backup : + testHelper + .getClient() + .getDatabaseAdminClient() + .listBackups(testHelper.getInstanceId().getInstance()) + .iterateAll()) { + logger.log(Level.FINE, "Deleting backup {0}", backup.getId()); + backup.delete(); + } + } logger.log(Level.FINE, "Deleting test instance {0}", testHelper.getInstanceId()); instanceAdminClient.deleteInstance(testHelper.getInstanceId().getInstance()); logger.log(Level.INFO, "Deleted test instance {0}", testHelper.getInstanceId()); From 730d6e91a0496130b75a61b144588f2f7c0cc5ec Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Tue, 27 Oct 2020 20:46:27 -0700 Subject: [PATCH 16/18] chore: regenerate README (#556) --- .github/readme/synth.metadata/synth.metadata | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/readme/synth.metadata/synth.metadata b/.github/readme/synth.metadata/synth.metadata index 93e513fffaa..63e2e023743 100644 --- a/.github/readme/synth.metadata/synth.metadata +++ b/.github/readme/synth.metadata/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "226c4f52aa252b9655ad39bf7825b490cf6de782" + "sha": "acf52a2df9dc8ccc7f8b01bfebad474471196955" } }, { diff --git a/README.md b/README.md index cad5241bab5..4b638e7ee6e 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ If you are using Maven without BOM, add this to your dependencies: com.google.cloud google-cloud-spanner - 2.0.2 + 3.0.0 ``` From d4ae85c91c2bda3f46cab8c9f7a4033ddd639c94 Mon Sep 17 00:00:00 2001 From: Thiago Nunes Date: Thu, 29 Oct 2020 08:45:01 +1100 Subject: [PATCH 17/18] fix: adds assembly descriptor to snippets samples (#559) Creates a jar with dependencies for the snippets samples. This is how we executed the samples in the other repository, so we are mimicking the process here. --- samples/snippets/assembly-descriptor.xml | 27 ++++++ samples/snippets/pom.xml | 111 ++++++++++++++++------- 2 files changed, 104 insertions(+), 34 deletions(-) create mode 100644 samples/snippets/assembly-descriptor.xml diff --git a/samples/snippets/assembly-descriptor.xml b/samples/snippets/assembly-descriptor.xml new file mode 100644 index 00000000000..8a9e7f8f500 --- /dev/null +++ b/samples/snippets/assembly-descriptor.xml @@ -0,0 +1,27 @@ + + jar-with-dependencies + + jar + + false + + + / + false + true + + + io.grpc.LoadBalancerProvider + + + + + + + ${project.build.outputDirectory} + . + + + diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 3bec4e2dad0..732c4a9bdf5 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -1,5 +1,7 @@ - + 4.0.0 com.google.cloud spanner-snippets @@ -44,49 +46,49 @@ google-cloud-spanner - + - io.opencensus - opencensus-api - ${opencensus.version} + io.opencensus + opencensus-api + ${opencensus.version} - io.opencensus - opencensus-impl - ${opencensus.version} - runtime + io.opencensus + opencensus-impl + ${opencensus.version} + runtime - io.opencensus - opencensus-contrib-zpages - ${opencensus.version} + io.opencensus + opencensus-contrib-zpages + ${opencensus.version} - io.opencensus - opencensus-exporter-trace-stackdriver - ${opencensus.version} - - - com.google.cloud - google-cloud-trace - - + io.opencensus + opencensus-exporter-trace-stackdriver + ${opencensus.version} + + + com.google.cloud + google-cloud-trace + + - io.opencensus - opencensus-exporter-stats-stackdriver - ${opencensus.version} - - - com.google.cloud - google-cloud-monitoring - - + io.opencensus + opencensus-exporter-stats-stackdriver + ${opencensus.version} + + + com.google.cloud + google-cloud-monitoring + + - io.opencensus - opencensus-contrib-grpc-metrics - ${opencensus.version} + io.opencensus + opencensus-contrib-grpc-metrics + ${opencensus.version} @@ -98,7 +100,7 @@ com.google.cloud google-cloud-monitoring - + junit junit @@ -112,4 +114,45 @@ test + + spanner-google-cloud-samples + + + maven-assembly-plugin + 3.3.0 + + + assembly-descriptor.xml + + + + com.example.spanner.SpannerSample + + + + + + make-assembly + package + + single + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.0.0-M5 + + + default-instance + mysample + quickstart-db + + + + + + From c304f40845a670c1c8654c447fb18b415a5f1b52 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 29 Oct 2020 15:01:02 +1100 Subject: [PATCH 18/18] chore: release 3.0.1 (#550) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 15 +++++++++++++++ google-cloud-spanner-bom/pom.xml | 18 +++++++++--------- google-cloud-spanner/pom.xml | 4 ++-- .../pom.xml | 4 ++-- .../pom.xml | 4 ++-- grpc-google-cloud-spanner-v1/pom.xml | 4 ++-- pom.xml | 16 ++++++++-------- .../pom.xml | 4 ++-- .../pom.xml | 4 ++-- proto-google-cloud-spanner-v1/pom.xml | 4 ++-- samples/snapshot/pom.xml | 2 +- versions.txt | 14 +++++++------- 12 files changed, 54 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 197f825b36b..dba5f433f08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +### [3.0.1](https://www.github.com/googleapis/java-spanner/compare/v3.0.0...v3.0.1) (2020-10-28) + + +### Bug Fixes + +* adds assembly descriptor to snippets samples ([#559](https://www.github.com/googleapis/java-spanner/issues/559)) ([d4ae85c](https://www.github.com/googleapis/java-spanner/commit/d4ae85c91c2bda3f46cab8c9f7a4033ddd639c94)) +* always delete all backups from an owned test instance ([#557](https://www.github.com/googleapis/java-spanner/issues/557)) ([ff571b0](https://www.github.com/googleapis/java-spanner/commit/ff571b01b9dffdda44a9bd322e04ff04b5b5c57a)), closes [#542](https://www.github.com/googleapis/java-spanner/issues/542) +* fixes the code of conduct document ([#541](https://www.github.com/googleapis/java-spanner/issues/541)) ([7b9d1db](https://www.github.com/googleapis/java-spanner/commit/7b9d1db28b7037d6b18df88f00b9213f2f6dab80)) +* SessionNotFound was not retried for AsyncTransactionManager ([#552](https://www.github.com/googleapis/java-spanner/issues/552)) ([5969f83](https://www.github.com/googleapis/java-spanner/commit/5969f8313a4df6ece63ee8f14df98cbc8511f026)) + + +### Dependencies + +* update dependency com.google.cloud:google-cloud-shared-dependencies to v0.13.0 ([#521](https://www.github.com/googleapis/java-spanner/issues/521)) ([0f4c017](https://www.github.com/googleapis/java-spanner/commit/0f4c017f112478ffc7dd15b0b234a9c48cd55a6e)) + ## [3.0.0](https://www.github.com/googleapis/java-spanner/compare/v2.0.2...v3.0.0) (2020-10-23) diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index b6f76cf64c4..e5e63aa3d55 100644 --- a/google-cloud-spanner-bom/pom.xml +++ b/google-cloud-spanner-bom/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-spanner-bom - 3.0.1-SNAPSHOT + 3.0.1 pom com.google.cloud @@ -64,43 +64,43 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 3.0.1-SNAPSHOT + 3.0.1 com.google.api.grpc grpc-google-cloud-spanner-v1 - 3.0.1-SNAPSHOT + 3.0.1 com.google.api.grpc proto-google-cloud-spanner-v1 - 3.0.1-SNAPSHOT + 3.0.1 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 3.0.1-SNAPSHOT + 3.0.1 com.google.cloud google-cloud-spanner - 3.0.1-SNAPSHOT + 3.0.1 com.google.cloud google-cloud-spanner test-jar - 3.0.1-SNAPSHOT + 3.0.1 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 3.0.1-SNAPSHOT + 3.0.1 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 3.0.1-SNAPSHOT + 3.0.1
diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index 9fd9cbdf930..f9a17860601 100644 --- a/google-cloud-spanner/pom.xml +++ b/google-cloud-spanner/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-spanner - 3.0.1-SNAPSHOT + 3.0.1 jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 3.0.1-SNAPSHOT + 3.0.1 google-cloud-spanner diff --git a/grpc-google-cloud-spanner-admin-database-v1/pom.xml b/grpc-google-cloud-spanner-admin-database-v1/pom.xml index 8cc769e0f5a..b1490eeb753 100644 --- a/grpc-google-cloud-spanner-admin-database-v1/pom.xml +++ b/grpc-google-cloud-spanner-admin-database-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 3.0.1-SNAPSHOT + 3.0.1 grpc-google-cloud-spanner-admin-database-v1 GRPC library for grpc-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 3.0.1-SNAPSHOT + 3.0.1 diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml index 63ab087f58f..509f7ac9247 100644 --- a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml +++ b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 3.0.1-SNAPSHOT + 3.0.1 grpc-google-cloud-spanner-admin-instance-v1 GRPC library for grpc-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 3.0.1-SNAPSHOT + 3.0.1 diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml index ce6f71ae027..153ffef666b 100644 --- a/grpc-google-cloud-spanner-v1/pom.xml +++ b/grpc-google-cloud-spanner-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 3.0.1-SNAPSHOT + 3.0.1 grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 3.0.1-SNAPSHOT + 3.0.1 diff --git a/pom.xml b/pom.xml index 59701286415..9b40f867f3c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-spanner-parent pom - 3.0.1-SNAPSHOT + 3.0.1 Google Cloud Spanner Parent https://github.com/googleapis/java-spanner @@ -71,37 +71,37 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 3.0.1-SNAPSHOT + 3.0.1 com.google.api.grpc proto-google-cloud-spanner-v1 - 3.0.1-SNAPSHOT + 3.0.1 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 3.0.1-SNAPSHOT + 3.0.1 com.google.api.grpc grpc-google-cloud-spanner-v1 - 3.0.1-SNAPSHOT + 3.0.1 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 3.0.1-SNAPSHOT + 3.0.1 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 3.0.1-SNAPSHOT + 3.0.1 com.google.cloud google-cloud-spanner - 3.0.1-SNAPSHOT + 3.0.1 diff --git a/proto-google-cloud-spanner-admin-database-v1/pom.xml b/proto-google-cloud-spanner-admin-database-v1/pom.xml index b256685bb67..e30ca328a67 100644 --- a/proto-google-cloud-spanner-admin-database-v1/pom.xml +++ b/proto-google-cloud-spanner-admin-database-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 3.0.1-SNAPSHOT + 3.0.1 proto-google-cloud-spanner-admin-database-v1 PROTO library for proto-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 3.0.1-SNAPSHOT + 3.0.1 diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml index dcad0353ff6..be7456a2d45 100644 --- a/proto-google-cloud-spanner-admin-instance-v1/pom.xml +++ b/proto-google-cloud-spanner-admin-instance-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 3.0.1-SNAPSHOT + 3.0.1 proto-google-cloud-spanner-admin-instance-v1 PROTO library for proto-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 3.0.1-SNAPSHOT + 3.0.1 diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml index 958b9821865..a725b93d400 100644 --- a/proto-google-cloud-spanner-v1/pom.xml +++ b/proto-google-cloud-spanner-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 3.0.1-SNAPSHOT + 3.0.1 proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 3.0.1-SNAPSHOT + 3.0.1 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 9fb9876e762..ed57fb6068f 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -31,7 +31,7 @@ com.google.cloud google-cloud-spanner - 3.0.1-SNAPSHOT + 3.0.1 diff --git a/versions.txt b/versions.txt index 8ed8d5a3979..185ea185120 100644 --- a/versions.txt +++ b/versions.txt @@ -1,10 +1,10 @@ # Format: # module:released-version:current-version -proto-google-cloud-spanner-admin-instance-v1:3.0.0:3.0.1-SNAPSHOT -proto-google-cloud-spanner-v1:3.0.0:3.0.1-SNAPSHOT -proto-google-cloud-spanner-admin-database-v1:3.0.0:3.0.1-SNAPSHOT -grpc-google-cloud-spanner-v1:3.0.0:3.0.1-SNAPSHOT -grpc-google-cloud-spanner-admin-instance-v1:3.0.0:3.0.1-SNAPSHOT -grpc-google-cloud-spanner-admin-database-v1:3.0.0:3.0.1-SNAPSHOT -google-cloud-spanner:3.0.0:3.0.1-SNAPSHOT \ No newline at end of file +proto-google-cloud-spanner-admin-instance-v1:3.0.1:3.0.1 +proto-google-cloud-spanner-v1:3.0.1:3.0.1 +proto-google-cloud-spanner-admin-database-v1:3.0.1:3.0.1 +grpc-google-cloud-spanner-v1:3.0.1:3.0.1 +grpc-google-cloud-spanner-admin-instance-v1:3.0.1:3.0.1 +grpc-google-cloud-spanner-admin-database-v1:3.0.1:3.0.1 +google-cloud-spanner:3.0.1:3.0.1 \ No newline at end of file