From 64bc8c3ac403531a8165f93fddbc6aba580ce610 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 18 Jan 2021 09:36:04 +0000 Subject: [PATCH 01/42] chore: release 3.3.3-SNAPSHOT (#811) :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). See [documentation](https://github.com/googleapis/release-please#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 2e6d7695172..41e4a0c513c 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.3.2 + 3.3.3-SNAPSHOT pom com.google.cloud @@ -64,43 +64,43 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 3.3.2 + 3.3.3-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-v1 - 3.3.2 + 3.3.3-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-v1 - 3.3.2 + 3.3.3-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 3.3.2 + 3.3.3-SNAPSHOT com.google.cloud google-cloud-spanner - 3.3.2 + 3.3.3-SNAPSHOT com.google.cloud google-cloud-spanner test-jar - 3.3.2 + 3.3.3-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 3.3.2 + 3.3.3-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 3.3.2 + 3.3.3-SNAPSHOT diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index f27a5be556d..0ca5a2b953b 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.3.2 + 3.3.3-SNAPSHOT jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 3.3.2 + 3.3.3-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 1a7a0d889fb..869511d6101 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.3.2 + 3.3.3-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.3.2 + 3.3.3-SNAPSHOT diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml index f51f2e89dc2..f58baec738d 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.3.2 + 3.3.3-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.3.2 + 3.3.3-SNAPSHOT diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml index 059e8e02945..34324a8acd2 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.3.2 + 3.3.3-SNAPSHOT grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 3.3.2 + 3.3.3-SNAPSHOT diff --git a/pom.xml b/pom.xml index f8d4aa31c19..11254547acb 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-spanner-parent pom - 3.3.2 + 3.3.3-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.3.2 + 3.3.3-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-v1 - 3.3.2 + 3.3.3-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 3.3.2 + 3.3.3-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-v1 - 3.3.2 + 3.3.3-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 3.3.2 + 3.3.3-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 3.3.2 + 3.3.3-SNAPSHOT com.google.cloud google-cloud-spanner - 3.3.2 + 3.3.3-SNAPSHOT diff --git a/proto-google-cloud-spanner-admin-database-v1/pom.xml b/proto-google-cloud-spanner-admin-database-v1/pom.xml index f4c05bbb57f..8b046befb56 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.3.2 + 3.3.3-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.3.2 + 3.3.3-SNAPSHOT diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml index 26298e14ca7..fd769578fe2 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.3.2 + 3.3.3-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.3.2 + 3.3.3-SNAPSHOT diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml index e2c914656e7..b30399bf7cf 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.3.2 + 3.3.3-SNAPSHOT proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 3.3.2 + 3.3.3-SNAPSHOT diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 6dbbf8567bc..bec35d771e8 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -31,7 +31,7 @@ com.google.cloud google-cloud-spanner - 3.3.2 + 3.3.3-SNAPSHOT diff --git a/versions.txt b/versions.txt index 0664f8f8d34..e458c81d7ac 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.3.2:3.3.2 -proto-google-cloud-spanner-v1:3.3.2:3.3.2 -proto-google-cloud-spanner-admin-database-v1:3.3.2:3.3.2 -grpc-google-cloud-spanner-v1:3.3.2:3.3.2 -grpc-google-cloud-spanner-admin-instance-v1:3.3.2:3.3.2 -grpc-google-cloud-spanner-admin-database-v1:3.3.2:3.3.2 -google-cloud-spanner:3.3.2:3.3.2 \ No newline at end of file +proto-google-cloud-spanner-admin-instance-v1:3.3.2:3.3.3-SNAPSHOT +proto-google-cloud-spanner-v1:3.3.2:3.3.3-SNAPSHOT +proto-google-cloud-spanner-admin-database-v1:3.3.2:3.3.3-SNAPSHOT +grpc-google-cloud-spanner-v1:3.3.2:3.3.3-SNAPSHOT +grpc-google-cloud-spanner-admin-instance-v1:3.3.2:3.3.3-SNAPSHOT +grpc-google-cloud-spanner-admin-database-v1:3.3.2:3.3.3-SNAPSHOT +google-cloud-spanner:3.3.2:3.3.3-SNAPSHOT \ No newline at end of file From df2b8df20217293045ff3c2751a134ca33468dd6 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Mon, 18 Jan 2021 03:12:02 -0800 Subject: [PATCH 02/42] chore: regenerate README (#813) This PR was generated using Autosynth. :rainbow:
Log from Synthtool ``` 2021-01-18 09:46:57,836 synthtool [DEBUG] > Executing /root/.cache/synthtool/java-spanner/.github/readme/synth.py. On branch autosynth-readme nothing to commit, working tree clean 2021-01-18 09:46:58,779 synthtool [DEBUG] > Wrote metadata to .github/readme/synth.metadata/synth.metadata. ```
Full log will be available here: https://source.cloud.google.com/results/invocations/4a681427-c53c-478a-ab6d-62cdbfd4a9f7/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 c23ee933f30..1a1e67bcad1 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": "5f3a67ad3a5213fa9fca977ebe4abb50ccd3f593" + "sha": "64bc8c3ac403531a8165f93fddbc6aba580ce610" } }, { diff --git a/README.md b/README.md index 4228838d64e..f5db3b08a64 100644 --- a/README.md +++ b/README.md @@ -51,12 +51,12 @@ compile 'com.google.cloud:google-cloud-spanner' ``` If you are using Gradle without BOM, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-spanner:3.3.1' +compile 'com.google.cloud:google-cloud-spanner:3.3.2' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "3.3.1" +libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "3.3.2" ``` ## Authentication From fadb4011e554d0554332ac47e1c0060afca9ffeb Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 19 Jan 2021 00:19:05 +0100 Subject: [PATCH 03/42] chore(deps): update dependency com.google.cloud:google-cloud-spanner to v3.3.2 (#812) --- 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 46b615b5cf2..9be81c3b67c 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 - 3.3.1 + 3.3.2 From 4aa770ce669404ad0eca542ae5bd32a421748c18 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Mon, 18 Jan 2021 15:54:03 -0800 Subject: [PATCH 04/42] chore: regenerate README (#814) This PR was generated using Autosynth. :rainbow:
Log from Synthtool ``` 2021-01-18 23:21:19,860 synthtool [DEBUG] > Executing /root/.cache/synthtool/java-spanner/.github/readme/synth.py. On branch autosynth-readme nothing to commit, working tree clean 2021-01-18 23:21:20,804 synthtool [DEBUG] > Wrote metadata to .github/readme/synth.metadata/synth.metadata. ```
Full log will be available here: https://source.cloud.google.com/results/invocations/daacbf96-4788-473a-9677-036a2ff8bd8c/targets - [ ] To automatically regenerate this PR, check this box. --- .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 1a1e67bcad1..54018735ad9 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": "64bc8c3ac403531a8165f93fddbc6aba580ce610" + "sha": "fadb4011e554d0554332ac47e1c0060afca9ffeb" } }, { diff --git a/README.md b/README.md index f5db3b08a64..1ddee68fa74 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 - 3.3.1 + 3.3.2 ``` From 80d3585870b81949ec641291e5a88fe391f78e27 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Fri, 22 Jan 2021 16:55:42 -0800 Subject: [PATCH 05/42] feat: add option for returning Spanner commit stats (#817) PiperOrigin-RevId: 353145174 Source-Author: Google APIs Source-Date: Thu Jan 21 18:06:01 2021 -0800 Source-Repo: googleapis/googleapis Source-Sha: 8fa381b7138f1d72966ff20563efae1b2194d359 Source-Link: https://github.com/googleapis/googleapis/commit/8fa381b7138f1d72966ff20563efae1b2194d359 --- .../cloud/spanner/v1/SpannerClient.java | 30 + .../cloud/spanner/v1/SpannerClientTest.java | 20 +- .../com/google/spanner/v1/SpannerGrpc.java | 20 + .../com/google/spanner/v1/CommitRequest.java | 98 ++ .../spanner/v1/CommitRequestOrBuilder.java | 15 + .../com/google/spanner/v1/CommitResponse.java | 882 ++++++++++++++++++ .../spanner/v1/CommitResponseOrBuilder.java | 41 + .../google/spanner/v1/ExecuteSqlRequest.java | 87 +- .../v1/ExecuteSqlRequestOrBuilder.java | 15 +- .../java/com/google/spanner/v1/Session.java | 128 ++- .../google/spanner/v1/SessionOrBuilder.java | 31 +- .../com/google/spanner/v1/SpannerProto.java | 407 ++++---- .../com/google/spanner/v1/StructType.java | 18 +- .../google/spanner/v1/TransactionOptions.java | 18 +- .../proto/google/spanner/v1/spanner.proto | 46 +- .../proto/google/spanner/v1/transaction.proto | 9 +- .../main/proto/google/spanner/v1/type.proto | 2 +- synth.metadata | 6 +- 18 files changed, 1558 insertions(+), 315 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/SpannerClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/SpannerClient.java index 46b124968cd..2c430faaade 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/SpannerClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/SpannerClient.java @@ -724,6 +724,11 @@ public final UnaryCallable beginTransactio * reasons. If `Commit` returns `ABORTED`, the caller should re-attempt the transaction from the * beginning, re-using the same session. * + *

On very rare occasions, `Commit` might return `UNKNOWN`. This can happen, for example, if + * the client job experiences a 1+ hour networking failure. At that point, Cloud Spanner has lost + * track of the transaction outcome and we recommend that you perform another read from the + * database to see the state of things as they are now. + * * @param session Required. The session in which the transaction to be committed is running. * @param transactionId Commit a previously-started transaction. * @param mutations The mutations to be executed when this transaction commits. All mutations are @@ -751,6 +756,11 @@ public final CommitResponse commit( * reasons. If `Commit` returns `ABORTED`, the caller should re-attempt the transaction from the * beginning, re-using the same session. * + *

On very rare occasions, `Commit` might return `UNKNOWN`. This can happen, for example, if + * the client job experiences a 1+ hour networking failure. At that point, Cloud Spanner has lost + * track of the transaction outcome and we recommend that you perform another read from the + * database to see the state of things as they are now. + * * @param session Required. The session in which the transaction to be committed is running. * @param singleUseTransaction Execute mutations in a temporary transaction. Note that unlike * commit of a previously-started transaction, commit with a temporary transaction is @@ -784,6 +794,11 @@ public final CommitResponse commit( * reasons. If `Commit` returns `ABORTED`, the caller should re-attempt the transaction from the * beginning, re-using the same session. * + *

On very rare occasions, `Commit` might return `UNKNOWN`. This can happen, for example, if + * the client job experiences a 1+ hour networking failure. At that point, Cloud Spanner has lost + * track of the transaction outcome and we recommend that you perform another read from the + * database to see the state of things as they are now. + * * @param session Required. The session in which the transaction to be committed is running. * @param transactionId Commit a previously-started transaction. * @param mutations The mutations to be executed when this transaction commits. All mutations are @@ -811,6 +826,11 @@ public final CommitResponse commit( * reasons. If `Commit` returns `ABORTED`, the caller should re-attempt the transaction from the * beginning, re-using the same session. * + *

On very rare occasions, `Commit` might return `UNKNOWN`. This can happen, for example, if + * the client job experiences a 1+ hour networking failure. At that point, Cloud Spanner has lost + * track of the transaction outcome and we recommend that you perform another read from the + * database to see the state of things as they are now. + * * @param session Required. The session in which the transaction to be committed is running. * @param singleUseTransaction Execute mutations in a temporary transaction. Note that unlike * commit of a previously-started transaction, commit with a temporary transaction is @@ -844,6 +864,11 @@ public final CommitResponse commit( * reasons. If `Commit` returns `ABORTED`, the caller should re-attempt the transaction from the * beginning, re-using the same session. * + *

On very rare occasions, `Commit` might return `UNKNOWN`. This can happen, for example, if + * the client job experiences a 1+ hour networking failure. At that point, Cloud Spanner has lost + * track of the transaction outcome and we recommend that you perform another read from the + * database to see the state of things as they are now. + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -861,6 +886,11 @@ public final CommitResponse commit(CommitRequest request) { * reasons. If `Commit` returns `ABORTED`, the caller should re-attempt the transaction from the * beginning, re-using the same session. * + *

On very rare occasions, `Commit` might return `UNKNOWN`. This can happen, for example, if + * the client job experiences a 1+ hour networking failure. At that point, Cloud Spanner has lost + * track of the transaction outcome and we recommend that you perform another read from the + * database to see the state of things as they are now. + * *

Sample code: */ public final UnaryCallable commitCallable() { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/v1/SpannerClientTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/v1/SpannerClientTest.java index a31cc5f2a19..0ee0e706d1f 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/v1/SpannerClientTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/v1/SpannerClientTest.java @@ -954,7 +954,10 @@ public void beginTransactionExceptionTest2() throws Exception { @Test public void commitTest() throws Exception { CommitResponse expectedResponse = - CommitResponse.newBuilder().setCommitTimestamp(Timestamp.newBuilder().build()).build(); + CommitResponse.newBuilder() + .setCommitTimestamp(Timestamp.newBuilder().build()) + .setCommitStats(CommitResponse.CommitStats.newBuilder().build()) + .build(); mockSpanner.addResponse(expectedResponse); SessionName session = SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]"); @@ -996,7 +999,10 @@ public void commitExceptionTest() throws Exception { @Test public void commitTest2() throws Exception { CommitResponse expectedResponse = - CommitResponse.newBuilder().setCommitTimestamp(Timestamp.newBuilder().build()).build(); + CommitResponse.newBuilder() + .setCommitTimestamp(Timestamp.newBuilder().build()) + .setCommitStats(CommitResponse.CommitStats.newBuilder().build()) + .build(); mockSpanner.addResponse(expectedResponse); SessionName session = SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]"); @@ -1038,7 +1044,10 @@ public void commitExceptionTest2() throws Exception { @Test public void commitTest3() throws Exception { CommitResponse expectedResponse = - CommitResponse.newBuilder().setCommitTimestamp(Timestamp.newBuilder().build()).build(); + CommitResponse.newBuilder() + .setCommitTimestamp(Timestamp.newBuilder().build()) + .setCommitStats(CommitResponse.CommitStats.newBuilder().build()) + .build(); mockSpanner.addResponse(expectedResponse); String session = "session1984987798"; @@ -1080,7 +1089,10 @@ public void commitExceptionTest3() throws Exception { @Test public void commitTest4() throws Exception { CommitResponse expectedResponse = - CommitResponse.newBuilder().setCommitTimestamp(Timestamp.newBuilder().build()).build(); + CommitResponse.newBuilder() + .setCommitTimestamp(Timestamp.newBuilder().build()) + .setCommitStats(CommitResponse.CommitStats.newBuilder().build()) + .build(); mockSpanner.addResponse(expectedResponse); String session = "session1984987798"; diff --git a/grpc-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SpannerGrpc.java b/grpc-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SpannerGrpc.java index 2ea1647918f..52e4daaaf13 100644 --- a/grpc-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SpannerGrpc.java +++ b/grpc-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SpannerGrpc.java @@ -924,6 +924,11 @@ public void beginTransaction( * transactions. However, it can also happen for a variety of other * reasons. If `Commit` returns `ABORTED`, the caller should re-attempt * the transaction from the beginning, re-using the same session. + * On very rare occasions, `Commit` might return `UNKNOWN`. This can happen, + * for example, if the client job experiences a 1+ hour networking failure. + * At that point, Cloud Spanner has lost track of the transaction outcome and + * we recommend that you perform another read from the database to see the + * state of things as they are now. * */ public void commit( @@ -1357,6 +1362,11 @@ public void beginTransaction( * transactions. However, it can also happen for a variety of other * reasons. If `Commit` returns `ABORTED`, the caller should re-attempt * the transaction from the beginning, re-using the same session. + * On very rare occasions, `Commit` might return `UNKNOWN`. This can happen, + * for example, if the client job experiences a 1+ hour networking failure. + * At that point, Cloud Spanner has lost track of the transaction outcome and + * we recommend that you perform another read from the database to see the + * state of things as they are now. * */ public void commit( @@ -1663,6 +1673,11 @@ public com.google.spanner.v1.Transaction beginTransaction( * transactions. However, it can also happen for a variety of other * reasons. If `Commit` returns `ABORTED`, the caller should re-attempt * the transaction from the beginning, re-using the same session. + * On very rare occasions, `Commit` might return `UNKNOWN`. This can happen, + * for example, if the client job experiences a 1+ hour networking failure. + * At that point, Cloud Spanner has lost track of the transaction outcome and + * we recommend that you perform another read from the database to see the + * state of things as they are now. * */ public com.google.spanner.v1.CommitResponse commit( @@ -1932,6 +1947,11 @@ public com.google.common.util.concurrent.ListenableFuture */ public com.google.common.util.concurrent.ListenableFuture diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequest.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequest.java index f322ec04c09..1ba7b235bbb 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequest.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequest.java @@ -111,6 +111,11 @@ private CommitRequest( input.readMessage(com.google.spanner.v1.Mutation.parser(), extensionRegistry)); break; } + case 40: + { + returnCommitStats_ = input.readBool(); + break; + } default: { if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) { @@ -422,6 +427,26 @@ public com.google.spanner.v1.MutationOrBuilder getMutationsOrBuilder(int index) return mutations_.get(index); } + public static final int RETURN_COMMIT_STATS_FIELD_NUMBER = 5; + private boolean returnCommitStats_; + /** + * + * + *

+   * If `true`, then statistics related to the transaction will be included in
+   * the [CommitResponse][google.spanner.v1.CommitResponse.commit_stats]. Default value is
+   * `false`.
+   * 
+ * + * bool return_commit_stats = 5; + * + * @return The returnCommitStats. + */ + @java.lang.Override + public boolean getReturnCommitStats() { + return returnCommitStats_; + } + private byte memoizedIsInitialized = -1; @java.lang.Override @@ -448,6 +473,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io for (int i = 0; i < mutations_.size(); i++) { output.writeMessage(4, mutations_.get(i)); } + if (returnCommitStats_ != false) { + output.writeBool(5, returnCommitStats_); + } unknownFields.writeTo(output); } @@ -473,6 +501,9 @@ public int getSerializedSize() { for (int i = 0; i < mutations_.size(); i++) { size += com.google.protobuf.CodedOutputStream.computeMessageSize(4, mutations_.get(i)); } + if (returnCommitStats_ != false) { + size += com.google.protobuf.CodedOutputStream.computeBoolSize(5, returnCommitStats_); + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -490,6 +521,7 @@ public boolean equals(final java.lang.Object obj) { if (!getSession().equals(other.getSession())) return false; if (!getMutationsList().equals(other.getMutationsList())) return false; + if (getReturnCommitStats() != other.getReturnCommitStats()) return false; if (!getTransactionCase().equals(other.getTransactionCase())) return false; switch (transactionCase_) { case 2: @@ -518,6 +550,8 @@ public int hashCode() { hash = (37 * hash) + MUTATIONS_FIELD_NUMBER; hash = (53 * hash) + getMutationsList().hashCode(); } + hash = (37 * hash) + RETURN_COMMIT_STATS_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(getReturnCommitStats()); switch (transactionCase_) { case 2: hash = (37 * hash) + TRANSACTION_ID_FIELD_NUMBER; @@ -684,6 +718,8 @@ public Builder clear() { } else { mutationsBuilder_.clear(); } + returnCommitStats_ = false; + transactionCase_ = 0; transaction_ = null; return this; @@ -733,6 +769,7 @@ public com.google.spanner.v1.CommitRequest buildPartial() { } else { result.mutations_ = mutationsBuilder_.build(); } + result.returnCommitStats_ = returnCommitStats_; result.transactionCase_ = transactionCase_; onBuilt(); return result; @@ -814,6 +851,9 @@ public Builder mergeFrom(com.google.spanner.v1.CommitRequest other) { } } } + if (other.getReturnCommitStats() != false) { + setReturnCommitStats(other.getReturnCommitStats()); + } switch (other.getTransactionCase()) { case TRANSACTION_ID: { @@ -1712,6 +1752,64 @@ public java.util.List getMutationsBuilde return mutationsBuilder_; } + private boolean returnCommitStats_; + /** + * + * + *
+     * If `true`, then statistics related to the transaction will be included in
+     * the [CommitResponse][google.spanner.v1.CommitResponse.commit_stats]. Default value is
+     * `false`.
+     * 
+ * + * bool return_commit_stats = 5; + * + * @return The returnCommitStats. + */ + @java.lang.Override + public boolean getReturnCommitStats() { + return returnCommitStats_; + } + /** + * + * + *
+     * If `true`, then statistics related to the transaction will be included in
+     * the [CommitResponse][google.spanner.v1.CommitResponse.commit_stats]. Default value is
+     * `false`.
+     * 
+ * + * bool return_commit_stats = 5; + * + * @param value The returnCommitStats to set. + * @return This builder for chaining. + */ + public Builder setReturnCommitStats(boolean value) { + + returnCommitStats_ = value; + onChanged(); + return this; + } + /** + * + * + *
+     * If `true`, then statistics related to the transaction will be included in
+     * the [CommitResponse][google.spanner.v1.CommitResponse.commit_stats]. Default value is
+     * `false`.
+     * 
+ * + * bool return_commit_stats = 5; + * + * @return This builder for chaining. + */ + public Builder clearReturnCommitStats() { + + returnCommitStats_ = false; + onChanged(); + return this; + } + @java.lang.Override public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequestOrBuilder.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequestOrBuilder.java index bb0f6c6acdd..a32fa4b4396 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequestOrBuilder.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequestOrBuilder.java @@ -185,5 +185,20 @@ public interface CommitRequestOrBuilder */ com.google.spanner.v1.MutationOrBuilder getMutationsOrBuilder(int index); + /** + * + * + *
+   * If `true`, then statistics related to the transaction will be included in
+   * the [CommitResponse][google.spanner.v1.CommitResponse.commit_stats]. Default value is
+   * `false`.
+   * 
+ * + * bool return_commit_stats = 5; + * + * @return The returnCommitStats. + */ + boolean getReturnCommitStats(); + public com.google.spanner.v1.CommitRequest.TransactionCase getTransactionCase(); } diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponse.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponse.java index 28772858452..320d165cf94 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponse.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponse.java @@ -81,6 +81,22 @@ private CommitResponse( commitTimestamp_ = subBuilder.buildPartial(); } + break; + } + case 18: + { + com.google.spanner.v1.CommitResponse.CommitStats.Builder subBuilder = null; + if (commitStats_ != null) { + subBuilder = commitStats_.toBuilder(); + } + commitStats_ = + input.readMessage( + com.google.spanner.v1.CommitResponse.CommitStats.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(commitStats_); + commitStats_ = subBuilder.buildPartial(); + } + break; } default: @@ -117,6 +133,586 @@ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { com.google.spanner.v1.CommitResponse.Builder.class); } + public interface CommitStatsOrBuilder + extends + // @@protoc_insertion_point(interface_extends:google.spanner.v1.CommitResponse.CommitStats) + com.google.protobuf.MessageOrBuilder { + + /** + * + * + *
+     * The total number of mutations for the transaction. Knowing the
+     * `mutation_count` value can help you maximize the number of mutations
+     * in a transaction and minimize the number of API round trips. You can
+     * also monitor this value to prevent transactions from exceeding the system
+     * [limit](http://cloud.google.com/spanner/quotas#limits_for_creating_reading_updating_and_deleting_data).
+     * If the number of mutations exceeds the limit, the server returns
+     * [INVALID_ARGUMENT](http://cloud.google.com/spanner/docs/reference/rest/v1/Code#ENUM_VALUES.INVALID_ARGUMENT).
+     * 
+ * + * int64 mutation_count = 1; + * + * @return The mutationCount. + */ + long getMutationCount(); + } + /** + * + * + *
+   * Additional statistics about a commit.
+   * 
+ * + * Protobuf type {@code google.spanner.v1.CommitResponse.CommitStats} + */ + public static final class CommitStats extends com.google.protobuf.GeneratedMessageV3 + implements + // @@protoc_insertion_point(message_implements:google.spanner.v1.CommitResponse.CommitStats) + CommitStatsOrBuilder { + private static final long serialVersionUID = 0L; + // Use CommitStats.newBuilder() to construct. + private CommitStats(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + + private CommitStats() {} + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance(UnusedPrivateParameter unused) { + return new CommitStats(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet getUnknownFields() { + return this.unknownFields; + } + + private CommitStats( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: + { + mutationCount_ = input.readInt64(); + break; + } + default: + { + if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.spanner.v1.SpannerProto + .internal_static_google_spanner_v1_CommitResponse_CommitStats_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.spanner.v1.SpannerProto + .internal_static_google_spanner_v1_CommitResponse_CommitStats_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.spanner.v1.CommitResponse.CommitStats.class, + com.google.spanner.v1.CommitResponse.CommitStats.Builder.class); + } + + public static final int MUTATION_COUNT_FIELD_NUMBER = 1; + private long mutationCount_; + /** + * + * + *
+     * The total number of mutations for the transaction. Knowing the
+     * `mutation_count` value can help you maximize the number of mutations
+     * in a transaction and minimize the number of API round trips. You can
+     * also monitor this value to prevent transactions from exceeding the system
+     * [limit](http://cloud.google.com/spanner/quotas#limits_for_creating_reading_updating_and_deleting_data).
+     * If the number of mutations exceeds the limit, the server returns
+     * [INVALID_ARGUMENT](http://cloud.google.com/spanner/docs/reference/rest/v1/Code#ENUM_VALUES.INVALID_ARGUMENT).
+     * 
+ * + * int64 mutation_count = 1; + * + * @return The mutationCount. + */ + @java.lang.Override + public long getMutationCount() { + return mutationCount_; + } + + private byte memoizedIsInitialized = -1; + + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { + if (mutationCount_ != 0L) { + output.writeInt64(1, mutationCount_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (mutationCount_ != 0L) { + size += com.google.protobuf.CodedOutputStream.computeInt64Size(1, mutationCount_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.google.spanner.v1.CommitResponse.CommitStats)) { + return super.equals(obj); + } + com.google.spanner.v1.CommitResponse.CommitStats other = + (com.google.spanner.v1.CommitResponse.CommitStats) obj; + + if (getMutationCount() != other.getMutationCount()) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + MUTATION_COUNT_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong(getMutationCount()); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseDelimitedFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseDelimitedFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + com.google.protobuf.CodedInputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder(com.google.spanner.v1.CommitResponse.CommitStats prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * + * + *
+     * Additional statistics about a commit.
+     * 
+ * + * Protobuf type {@code google.spanner.v1.CommitResponse.CommitStats} + */ + public static final class Builder + extends com.google.protobuf.GeneratedMessageV3.Builder + implements + // @@protoc_insertion_point(builder_implements:google.spanner.v1.CommitResponse.CommitStats) + com.google.spanner.v1.CommitResponse.CommitStatsOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.spanner.v1.SpannerProto + .internal_static_google_spanner_v1_CommitResponse_CommitStats_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.spanner.v1.SpannerProto + .internal_static_google_spanner_v1_CommitResponse_CommitStats_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.spanner.v1.CommitResponse.CommitStats.class, + com.google.spanner.v1.CommitResponse.CommitStats.Builder.class); + } + + // Construct using com.google.spanner.v1.CommitResponse.CommitStats.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) {} + } + + @java.lang.Override + public Builder clear() { + super.clear(); + mutationCount_ = 0L; + + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return com.google.spanner.v1.SpannerProto + .internal_static_google_spanner_v1_CommitResponse_CommitStats_descriptor; + } + + @java.lang.Override + public com.google.spanner.v1.CommitResponse.CommitStats getDefaultInstanceForType() { + return com.google.spanner.v1.CommitResponse.CommitStats.getDefaultInstance(); + } + + @java.lang.Override + public com.google.spanner.v1.CommitResponse.CommitStats build() { + com.google.spanner.v1.CommitResponse.CommitStats result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public com.google.spanner.v1.CommitResponse.CommitStats buildPartial() { + com.google.spanner.v1.CommitResponse.CommitStats result = + new com.google.spanner.v1.CommitResponse.CommitStats(this); + result.mutationCount_ = mutationCount_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.setField(field, value); + } + + @java.lang.Override + public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + + @java.lang.Override + public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, + java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.addRepeatedField(field, value); + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.google.spanner.v1.CommitResponse.CommitStats) { + return mergeFrom((com.google.spanner.v1.CommitResponse.CommitStats) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.google.spanner.v1.CommitResponse.CommitStats other) { + if (other == com.google.spanner.v1.CommitResponse.CommitStats.getDefaultInstance()) + return this; + if (other.getMutationCount() != 0L) { + setMutationCount(other.getMutationCount()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.spanner.v1.CommitResponse.CommitStats parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = + (com.google.spanner.v1.CommitResponse.CommitStats) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private long mutationCount_; + /** + * + * + *
+       * The total number of mutations for the transaction. Knowing the
+       * `mutation_count` value can help you maximize the number of mutations
+       * in a transaction and minimize the number of API round trips. You can
+       * also monitor this value to prevent transactions from exceeding the system
+       * [limit](http://cloud.google.com/spanner/quotas#limits_for_creating_reading_updating_and_deleting_data).
+       * If the number of mutations exceeds the limit, the server returns
+       * [INVALID_ARGUMENT](http://cloud.google.com/spanner/docs/reference/rest/v1/Code#ENUM_VALUES.INVALID_ARGUMENT).
+       * 
+ * + * int64 mutation_count = 1; + * + * @return The mutationCount. + */ + @java.lang.Override + public long getMutationCount() { + return mutationCount_; + } + /** + * + * + *
+       * The total number of mutations for the transaction. Knowing the
+       * `mutation_count` value can help you maximize the number of mutations
+       * in a transaction and minimize the number of API round trips. You can
+       * also monitor this value to prevent transactions from exceeding the system
+       * [limit](http://cloud.google.com/spanner/quotas#limits_for_creating_reading_updating_and_deleting_data).
+       * If the number of mutations exceeds the limit, the server returns
+       * [INVALID_ARGUMENT](http://cloud.google.com/spanner/docs/reference/rest/v1/Code#ENUM_VALUES.INVALID_ARGUMENT).
+       * 
+ * + * int64 mutation_count = 1; + * + * @param value The mutationCount to set. + * @return This builder for chaining. + */ + public Builder setMutationCount(long value) { + + mutationCount_ = value; + onChanged(); + return this; + } + /** + * + * + *
+       * The total number of mutations for the transaction. Knowing the
+       * `mutation_count` value can help you maximize the number of mutations
+       * in a transaction and minimize the number of API round trips. You can
+       * also monitor this value to prevent transactions from exceeding the system
+       * [limit](http://cloud.google.com/spanner/quotas#limits_for_creating_reading_updating_and_deleting_data).
+       * If the number of mutations exceeds the limit, the server returns
+       * [INVALID_ARGUMENT](http://cloud.google.com/spanner/docs/reference/rest/v1/Code#ENUM_VALUES.INVALID_ARGUMENT).
+       * 
+ * + * int64 mutation_count = 1; + * + * @return This builder for chaining. + */ + public Builder clearMutationCount() { + + mutationCount_ = 0L; + onChanged(); + return this; + } + + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + // @@protoc_insertion_point(builder_scope:google.spanner.v1.CommitResponse.CommitStats) + } + + // @@protoc_insertion_point(class_scope:google.spanner.v1.CommitResponse.CommitStats) + private static final com.google.spanner.v1.CommitResponse.CommitStats DEFAULT_INSTANCE; + + static { + DEFAULT_INSTANCE = new com.google.spanner.v1.CommitResponse.CommitStats(); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + @java.lang.Override + public CommitStats parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new CommitStats(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public com.google.spanner.v1.CommitResponse.CommitStats getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + } + public static final int COMMIT_TIMESTAMP_FIELD_NUMBER = 1; private com.google.protobuf.Timestamp commitTimestamp_; /** @@ -165,6 +761,60 @@ public com.google.protobuf.TimestampOrBuilder getCommitTimestampOrBuilder() { return getCommitTimestamp(); } + public static final int COMMIT_STATS_FIELD_NUMBER = 2; + private com.google.spanner.v1.CommitResponse.CommitStats commitStats_; + /** + * + * + *
+   * The statistics about this Commit. Not returned by default.
+   * For more information, see
+   * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+   * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + * + * @return Whether the commitStats field is set. + */ + @java.lang.Override + public boolean hasCommitStats() { + return commitStats_ != null; + } + /** + * + * + *
+   * The statistics about this Commit. Not returned by default.
+   * For more information, see
+   * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+   * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + * + * @return The commitStats. + */ + @java.lang.Override + public com.google.spanner.v1.CommitResponse.CommitStats getCommitStats() { + return commitStats_ == null + ? com.google.spanner.v1.CommitResponse.CommitStats.getDefaultInstance() + : commitStats_; + } + /** + * + * + *
+   * The statistics about this Commit. Not returned by default.
+   * For more information, see
+   * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+   * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + @java.lang.Override + public com.google.spanner.v1.CommitResponse.CommitStatsOrBuilder getCommitStatsOrBuilder() { + return getCommitStats(); + } + private byte memoizedIsInitialized = -1; @java.lang.Override @@ -182,6 +832,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io if (commitTimestamp_ != null) { output.writeMessage(1, getCommitTimestamp()); } + if (commitStats_ != null) { + output.writeMessage(2, getCommitStats()); + } unknownFields.writeTo(output); } @@ -194,6 +847,9 @@ public int getSerializedSize() { if (commitTimestamp_ != null) { size += com.google.protobuf.CodedOutputStream.computeMessageSize(1, getCommitTimestamp()); } + if (commitStats_ != null) { + size += com.google.protobuf.CodedOutputStream.computeMessageSize(2, getCommitStats()); + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -213,6 +869,10 @@ public boolean equals(final java.lang.Object obj) { if (hasCommitTimestamp()) { if (!getCommitTimestamp().equals(other.getCommitTimestamp())) return false; } + if (hasCommitStats() != other.hasCommitStats()) return false; + if (hasCommitStats()) { + if (!getCommitStats().equals(other.getCommitStats())) return false; + } if (!unknownFields.equals(other.unknownFields)) return false; return true; } @@ -228,6 +888,10 @@ public int hashCode() { hash = (37 * hash) + COMMIT_TIMESTAMP_FIELD_NUMBER; hash = (53 * hash) + getCommitTimestamp().hashCode(); } + if (hasCommitStats()) { + hash = (37 * hash) + COMMIT_STATS_FIELD_NUMBER; + hash = (53 * hash) + getCommitStats().hashCode(); + } hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; @@ -378,6 +1042,12 @@ public Builder clear() { commitTimestamp_ = null; commitTimestampBuilder_ = null; } + if (commitStatsBuilder_ == null) { + commitStats_ = null; + } else { + commitStats_ = null; + commitStatsBuilder_ = null; + } return this; } @@ -409,6 +1079,11 @@ public com.google.spanner.v1.CommitResponse buildPartial() { } else { result.commitTimestamp_ = commitTimestampBuilder_.build(); } + if (commitStatsBuilder_ == null) { + result.commitStats_ = commitStats_; + } else { + result.commitStats_ = commitStatsBuilder_.build(); + } onBuilt(); return result; } @@ -461,6 +1136,9 @@ public Builder mergeFrom(com.google.spanner.v1.CommitResponse other) { if (other.hasCommitTimestamp()) { mergeCommitTimestamp(other.getCommitTimestamp()); } + if (other.hasCommitStats()) { + mergeCommitStats(other.getCommitStats()); + } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; @@ -675,6 +1353,210 @@ public com.google.protobuf.TimestampOrBuilder getCommitTimestampOrBuilder() { return commitTimestampBuilder_; } + private com.google.spanner.v1.CommitResponse.CommitStats commitStats_; + private com.google.protobuf.SingleFieldBuilderV3< + com.google.spanner.v1.CommitResponse.CommitStats, + com.google.spanner.v1.CommitResponse.CommitStats.Builder, + com.google.spanner.v1.CommitResponse.CommitStatsOrBuilder> + commitStatsBuilder_; + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + * + * @return Whether the commitStats field is set. + */ + public boolean hasCommitStats() { + return commitStatsBuilder_ != null || commitStats_ != null; + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + * + * @return The commitStats. + */ + public com.google.spanner.v1.CommitResponse.CommitStats getCommitStats() { + if (commitStatsBuilder_ == null) { + return commitStats_ == null + ? com.google.spanner.v1.CommitResponse.CommitStats.getDefaultInstance() + : commitStats_; + } else { + return commitStatsBuilder_.getMessage(); + } + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + public Builder setCommitStats(com.google.spanner.v1.CommitResponse.CommitStats value) { + if (commitStatsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + commitStats_ = value; + onChanged(); + } else { + commitStatsBuilder_.setMessage(value); + } + + return this; + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + public Builder setCommitStats( + com.google.spanner.v1.CommitResponse.CommitStats.Builder builderForValue) { + if (commitStatsBuilder_ == null) { + commitStats_ = builderForValue.build(); + onChanged(); + } else { + commitStatsBuilder_.setMessage(builderForValue.build()); + } + + return this; + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + public Builder mergeCommitStats(com.google.spanner.v1.CommitResponse.CommitStats value) { + if (commitStatsBuilder_ == null) { + if (commitStats_ != null) { + commitStats_ = + com.google.spanner.v1.CommitResponse.CommitStats.newBuilder(commitStats_) + .mergeFrom(value) + .buildPartial(); + } else { + commitStats_ = value; + } + onChanged(); + } else { + commitStatsBuilder_.mergeFrom(value); + } + + return this; + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + public Builder clearCommitStats() { + if (commitStatsBuilder_ == null) { + commitStats_ = null; + onChanged(); + } else { + commitStats_ = null; + commitStatsBuilder_ = null; + } + + return this; + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + public com.google.spanner.v1.CommitResponse.CommitStats.Builder getCommitStatsBuilder() { + + onChanged(); + return getCommitStatsFieldBuilder().getBuilder(); + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + public com.google.spanner.v1.CommitResponse.CommitStatsOrBuilder getCommitStatsOrBuilder() { + if (commitStatsBuilder_ != null) { + return commitStatsBuilder_.getMessageOrBuilder(); + } else { + return commitStats_ == null + ? com.google.spanner.v1.CommitResponse.CommitStats.getDefaultInstance() + : commitStats_; + } + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + private com.google.protobuf.SingleFieldBuilderV3< + com.google.spanner.v1.CommitResponse.CommitStats, + com.google.spanner.v1.CommitResponse.CommitStats.Builder, + com.google.spanner.v1.CommitResponse.CommitStatsOrBuilder> + getCommitStatsFieldBuilder() { + if (commitStatsBuilder_ == null) { + commitStatsBuilder_ = + new com.google.protobuf.SingleFieldBuilderV3< + com.google.spanner.v1.CommitResponse.CommitStats, + com.google.spanner.v1.CommitResponse.CommitStats.Builder, + com.google.spanner.v1.CommitResponse.CommitStatsOrBuilder>( + getCommitStats(), getParentForChildren(), isClean()); + commitStats_ = null; + } + return commitStatsBuilder_; + } + @java.lang.Override public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponseOrBuilder.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponseOrBuilder.java index 90c6c6e6729..9ff076ff849 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponseOrBuilder.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponseOrBuilder.java @@ -57,4 +57,45 @@ public interface CommitResponseOrBuilder * .google.protobuf.Timestamp commit_timestamp = 1; */ com.google.protobuf.TimestampOrBuilder getCommitTimestampOrBuilder(); + + /** + * + * + *
+   * The statistics about this Commit. Not returned by default.
+   * For more information, see
+   * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+   * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + * + * @return Whether the commitStats field is set. + */ + boolean hasCommitStats(); + /** + * + * + *
+   * The statistics about this Commit. Not returned by default.
+   * For more information, see
+   * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+   * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + * + * @return The commitStats. + */ + com.google.spanner.v1.CommitResponse.CommitStats getCommitStats(); + /** + * + * + *
+   * The statistics about this Commit. Not returned by default.
+   * For more information, see
+   * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+   * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + com.google.spanner.v1.CommitResponse.CommitStatsOrBuilder getCommitStatsOrBuilder(); } diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ExecuteSqlRequest.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ExecuteSqlRequest.java index 1e757805fd5..79a7ad822b4 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ExecuteSqlRequest.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ExecuteSqlRequest.java @@ -402,6 +402,9 @@ public interface QueryOptionsOrBuilder * SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS. Executing a SQL statement * with an invalid optimizer version will fail with a syntax error * (`INVALID_ARGUMENT`) status. + * See + * https://cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer + * for more information on managing the query optimizer. * The `optimizer_version` statement hint has precedence over this setting. * * @@ -426,6 +429,9 @@ public interface QueryOptionsOrBuilder * SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS. Executing a SQL statement * with an invalid optimizer version will fail with a syntax error * (`INVALID_ARGUMENT`) status. + * See + * https://cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer + * for more information on managing the query optimizer. * The `optimizer_version` statement hint has precedence over this setting. * * @@ -546,6 +552,9 @@ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { * SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS. Executing a SQL statement * with an invalid optimizer version will fail with a syntax error * (`INVALID_ARGUMENT`) status. + * See + * https://cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer + * for more information on managing the query optimizer. * The `optimizer_version` statement hint has precedence over this setting. * * @@ -581,6 +590,9 @@ public java.lang.String getOptimizerVersion() { * SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS. Executing a SQL statement * with an invalid optimizer version will fail with a syntax error * (`INVALID_ARGUMENT`) status. + * See + * https://cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer + * for more information on managing the query optimizer. * The `optimizer_version` statement hint has precedence over this setting. * * @@ -941,6 +953,9 @@ public Builder mergeFrom( * SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS. Executing a SQL statement * with an invalid optimizer version will fail with a syntax error * (`INVALID_ARGUMENT`) status. + * See + * https://cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer + * for more information on managing the query optimizer. * The `optimizer_version` statement hint has precedence over this setting. * * @@ -975,6 +990,9 @@ public java.lang.String getOptimizerVersion() { * SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS. Executing a SQL statement * with an invalid optimizer version will fail with a syntax error * (`INVALID_ARGUMENT`) status. + * See + * https://cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer + * for more information on managing the query optimizer. * The `optimizer_version` statement hint has precedence over this setting. * * @@ -1009,6 +1027,9 @@ public com.google.protobuf.ByteString getOptimizerVersionBytes() { * SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS. Executing a SQL statement * with an invalid optimizer version will fail with a syntax error * (`INVALID_ARGUMENT`) status. + * See + * https://cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer + * for more information on managing the query optimizer. * The `optimizer_version` statement hint has precedence over this setting. * * @@ -1042,6 +1063,9 @@ public Builder setOptimizerVersion(java.lang.String value) { * SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS. Executing a SQL statement * with an invalid optimizer version will fail with a syntax error * (`INVALID_ARGUMENT`) status. + * See + * https://cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer + * for more information on managing the query optimizer. * The `optimizer_version` statement hint has precedence over this setting. * * @@ -1071,6 +1095,9 @@ public Builder clearOptimizerVersion() { * SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS. Executing a SQL statement * with an invalid optimizer version will fail with a syntax error * (`INVALID_ARGUMENT`) status. + * See + * https://cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer + * for more information on managing the query optimizer. * The `optimizer_version` statement hint has precedence over this setting. * * @@ -1318,8 +1345,9 @@ public com.google.protobuf.ByteString getSqlBytes() { *
    * Parameter names and values that bind to placeholders in the SQL string.
    * A parameter placeholder consists of the `@` character followed by the
-   * parameter name (for example, `@firstName`). Parameter names can contain
-   * letters, numbers, and underscores.
+   * parameter name (for example, `@firstName`). Parameter names must conform
+   * to the naming requirements of identifiers as specified at
+   * https://cloud.google.com/spanner/docs/lexical#identifiers.
    * Parameters can appear anywhere that a literal value is expected.  The same
    * parameter name can be used more than once, for example:
    * `"WHERE id > @msg_id AND id < @msg_id + 100"`
@@ -1340,8 +1368,9 @@ public boolean hasParams() {
    * 
    * Parameter names and values that bind to placeholders in the SQL string.
    * A parameter placeholder consists of the `@` character followed by the
-   * parameter name (for example, `@firstName`). Parameter names can contain
-   * letters, numbers, and underscores.
+   * parameter name (for example, `@firstName`). Parameter names must conform
+   * to the naming requirements of identifiers as specified at
+   * https://cloud.google.com/spanner/docs/lexical#identifiers.
    * Parameters can appear anywhere that a literal value is expected.  The same
    * parameter name can be used more than once, for example:
    * `"WHERE id > @msg_id AND id < @msg_id + 100"`
@@ -1362,8 +1391,9 @@ public com.google.protobuf.Struct getParams() {
    * 
    * Parameter names and values that bind to placeholders in the SQL string.
    * A parameter placeholder consists of the `@` character followed by the
-   * parameter name (for example, `@firstName`). Parameter names can contain
-   * letters, numbers, and underscores.
+   * parameter name (for example, `@firstName`). Parameter names must conform
+   * to the naming requirements of identifiers as specified at
+   * https://cloud.google.com/spanner/docs/lexical#identifiers.
    * Parameters can appear anywhere that a literal value is expected.  The same
    * parameter name can be used more than once, for example:
    * `"WHERE id > @msg_id AND id < @msg_id + 100"`
@@ -2656,8 +2686,9 @@ public Builder setSqlBytes(com.google.protobuf.ByteString value) {
      * 
      * Parameter names and values that bind to placeholders in the SQL string.
      * A parameter placeholder consists of the `@` character followed by the
-     * parameter name (for example, `@firstName`). Parameter names can contain
-     * letters, numbers, and underscores.
+     * parameter name (for example, `@firstName`). Parameter names must conform
+     * to the naming requirements of identifiers as specified at
+     * https://cloud.google.com/spanner/docs/lexical#identifiers.
      * Parameters can appear anywhere that a literal value is expected.  The same
      * parameter name can be used more than once, for example:
      * `"WHERE id > @msg_id AND id < @msg_id + 100"`
@@ -2677,8 +2708,9 @@ public boolean hasParams() {
      * 
      * Parameter names and values that bind to placeholders in the SQL string.
      * A parameter placeholder consists of the `@` character followed by the
-     * parameter name (for example, `@firstName`). Parameter names can contain
-     * letters, numbers, and underscores.
+     * parameter name (for example, `@firstName`). Parameter names must conform
+     * to the naming requirements of identifiers as specified at
+     * https://cloud.google.com/spanner/docs/lexical#identifiers.
      * Parameters can appear anywhere that a literal value is expected.  The same
      * parameter name can be used more than once, for example:
      * `"WHERE id > @msg_id AND id < @msg_id + 100"`
@@ -2702,8 +2734,9 @@ public com.google.protobuf.Struct getParams() {
      * 
      * Parameter names and values that bind to placeholders in the SQL string.
      * A parameter placeholder consists of the `@` character followed by the
-     * parameter name (for example, `@firstName`). Parameter names can contain
-     * letters, numbers, and underscores.
+     * parameter name (for example, `@firstName`). Parameter names must conform
+     * to the naming requirements of identifiers as specified at
+     * https://cloud.google.com/spanner/docs/lexical#identifiers.
      * Parameters can appear anywhere that a literal value is expected.  The same
      * parameter name can be used more than once, for example:
      * `"WHERE id > @msg_id AND id < @msg_id + 100"`
@@ -2731,8 +2764,9 @@ public Builder setParams(com.google.protobuf.Struct value) {
      * 
      * Parameter names and values that bind to placeholders in the SQL string.
      * A parameter placeholder consists of the `@` character followed by the
-     * parameter name (for example, `@firstName`). Parameter names can contain
-     * letters, numbers, and underscores.
+     * parameter name (for example, `@firstName`). Parameter names must conform
+     * to the naming requirements of identifiers as specified at
+     * https://cloud.google.com/spanner/docs/lexical#identifiers.
      * Parameters can appear anywhere that a literal value is expected.  The same
      * parameter name can be used more than once, for example:
      * `"WHERE id > @msg_id AND id < @msg_id + 100"`
@@ -2757,8 +2791,9 @@ public Builder setParams(com.google.protobuf.Struct.Builder builderForValue) {
      * 
      * Parameter names and values that bind to placeholders in the SQL string.
      * A parameter placeholder consists of the `@` character followed by the
-     * parameter name (for example, `@firstName`). Parameter names can contain
-     * letters, numbers, and underscores.
+     * parameter name (for example, `@firstName`). Parameter names must conform
+     * to the naming requirements of identifiers as specified at
+     * https://cloud.google.com/spanner/docs/lexical#identifiers.
      * Parameters can appear anywhere that a literal value is expected.  The same
      * parameter name can be used more than once, for example:
      * `"WHERE id > @msg_id AND id < @msg_id + 100"`
@@ -2787,8 +2822,9 @@ public Builder mergeParams(com.google.protobuf.Struct value) {
      * 
      * Parameter names and values that bind to placeholders in the SQL string.
      * A parameter placeholder consists of the `@` character followed by the
-     * parameter name (for example, `@firstName`). Parameter names can contain
-     * letters, numbers, and underscores.
+     * parameter name (for example, `@firstName`). Parameter names must conform
+     * to the naming requirements of identifiers as specified at
+     * https://cloud.google.com/spanner/docs/lexical#identifiers.
      * Parameters can appear anywhere that a literal value is expected.  The same
      * parameter name can be used more than once, for example:
      * `"WHERE id > @msg_id AND id < @msg_id + 100"`
@@ -2814,8 +2850,9 @@ public Builder clearParams() {
      * 
      * Parameter names and values that bind to placeholders in the SQL string.
      * A parameter placeholder consists of the `@` character followed by the
-     * parameter name (for example, `@firstName`). Parameter names can contain
-     * letters, numbers, and underscores.
+     * parameter name (for example, `@firstName`). Parameter names must conform
+     * to the naming requirements of identifiers as specified at
+     * https://cloud.google.com/spanner/docs/lexical#identifiers.
      * Parameters can appear anywhere that a literal value is expected.  The same
      * parameter name can be used more than once, for example:
      * `"WHERE id > @msg_id AND id < @msg_id + 100"`
@@ -2835,8 +2872,9 @@ public com.google.protobuf.Struct.Builder getParamsBuilder() {
      * 
      * Parameter names and values that bind to placeholders in the SQL string.
      * A parameter placeholder consists of the `@` character followed by the
-     * parameter name (for example, `@firstName`). Parameter names can contain
-     * letters, numbers, and underscores.
+     * parameter name (for example, `@firstName`). Parameter names must conform
+     * to the naming requirements of identifiers as specified at
+     * https://cloud.google.com/spanner/docs/lexical#identifiers.
      * Parameters can appear anywhere that a literal value is expected.  The same
      * parameter name can be used more than once, for example:
      * `"WHERE id > @msg_id AND id < @msg_id + 100"`
@@ -2858,8 +2896,9 @@ public com.google.protobuf.StructOrBuilder getParamsOrBuilder() {
      * 
      * Parameter names and values that bind to placeholders in the SQL string.
      * A parameter placeholder consists of the `@` character followed by the
-     * parameter name (for example, `@firstName`). Parameter names can contain
-     * letters, numbers, and underscores.
+     * parameter name (for example, `@firstName`). Parameter names must conform
+     * to the naming requirements of identifiers as specified at
+     * https://cloud.google.com/spanner/docs/lexical#identifiers.
      * Parameters can appear anywhere that a literal value is expected.  The same
      * parameter name can be used more than once, for example:
      * `"WHERE id > @msg_id AND id < @msg_id + 100"`
diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ExecuteSqlRequestOrBuilder.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ExecuteSqlRequestOrBuilder.java
index 7971beeb49c..d6af9b0ba6c 100644
--- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ExecuteSqlRequestOrBuilder.java
+++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ExecuteSqlRequestOrBuilder.java
@@ -136,8 +136,9 @@ public interface ExecuteSqlRequestOrBuilder
    * 
    * Parameter names and values that bind to placeholders in the SQL string.
    * A parameter placeholder consists of the `@` character followed by the
-   * parameter name (for example, `@firstName`). Parameter names can contain
-   * letters, numbers, and underscores.
+   * parameter name (for example, `@firstName`). Parameter names must conform
+   * to the naming requirements of identifiers as specified at
+   * https://cloud.google.com/spanner/docs/lexical#identifiers.
    * Parameters can appear anywhere that a literal value is expected.  The same
    * parameter name can be used more than once, for example:
    * `"WHERE id > @msg_id AND id < @msg_id + 100"`
@@ -155,8 +156,9 @@ public interface ExecuteSqlRequestOrBuilder
    * 
    * Parameter names and values that bind to placeholders in the SQL string.
    * A parameter placeholder consists of the `@` character followed by the
-   * parameter name (for example, `@firstName`). Parameter names can contain
-   * letters, numbers, and underscores.
+   * parameter name (for example, `@firstName`). Parameter names must conform
+   * to the naming requirements of identifiers as specified at
+   * https://cloud.google.com/spanner/docs/lexical#identifiers.
    * Parameters can appear anywhere that a literal value is expected.  The same
    * parameter name can be used more than once, for example:
    * `"WHERE id > @msg_id AND id < @msg_id + 100"`
@@ -174,8 +176,9 @@ public interface ExecuteSqlRequestOrBuilder
    * 
    * Parameter names and values that bind to placeholders in the SQL string.
    * A parameter placeholder consists of the `@` character followed by the
-   * parameter name (for example, `@firstName`). Parameter names can contain
-   * letters, numbers, and underscores.
+   * parameter name (for example, `@firstName`). Parameter names must conform
+   * to the naming requirements of identifiers as specified at
+   * https://cloud.google.com/spanner/docs/lexical#identifiers.
    * Parameters can appear anywhere that a literal value is expected.  The same
    * parameter name can be used more than once, for example:
    * `"WHERE id > @msg_id AND id < @msg_id + 100"`
diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/Session.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/Session.java
index 9fe4f10c7cb..1b058eb621f 100644
--- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/Session.java
+++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/Session.java
@@ -170,11 +170,10 @@ protected com.google.protobuf.MapField internalGetMapField(int number) {
    *
    *
    * 
-   * The name of the session. This is always system-assigned; values provided
-   * when creating a session are ignored.
+   * Output only. The name of the session. This is always system-assigned.
    * 
* - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = OUTPUT_ONLY]; * * @return The name. */ @@ -194,11 +193,10 @@ public java.lang.String getName() { * * *
-   * The name of the session. This is always system-assigned; values provided
-   * when creating a session are ignored.
+   * Output only. The name of the session. This is always system-assigned.
    * 
* - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = OUTPUT_ONLY]; * * @return The bytes for name. */ @@ -346,7 +344,8 @@ public java.lang.String getLabelsOrThrow(java.lang.String key) { * Output only. The timestamp when the session is created. *
* - * .google.protobuf.Timestamp create_time = 3; + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * * * @return Whether the createTime field is set. */ @@ -361,7 +360,8 @@ public boolean hasCreateTime() { * Output only. The timestamp when the session is created. *
* - * .google.protobuf.Timestamp create_time = 3; + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * * * @return The createTime. */ @@ -376,7 +376,8 @@ public com.google.protobuf.Timestamp getCreateTime() { * Output only. The timestamp when the session is created. *
* - * .google.protobuf.Timestamp create_time = 3; + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ @java.lang.Override public com.google.protobuf.TimestampOrBuilder getCreateTimeOrBuilder() { @@ -393,7 +394,9 @@ public com.google.protobuf.TimestampOrBuilder getCreateTimeOrBuilder() { * typically earlier than the actual last use time. *
* - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * * * @return Whether the approximateLastUseTime field is set. */ @@ -409,7 +412,9 @@ public boolean hasApproximateLastUseTime() { * typically earlier than the actual last use time. *
* - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * * * @return The approximateLastUseTime. */ @@ -427,7 +432,9 @@ public com.google.protobuf.Timestamp getApproximateLastUseTime() { * typically earlier than the actual last use time. *
* - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ @java.lang.Override public com.google.protobuf.TimestampOrBuilder getApproximateLastUseTimeOrBuilder() { @@ -852,11 +859,10 @@ public Builder mergeFrom( * * *
-     * The name of the session. This is always system-assigned; values provided
-     * when creating a session are ignored.
+     * Output only. The name of the session. This is always system-assigned.
      * 
* - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = OUTPUT_ONLY]; * * @return The name. */ @@ -875,11 +881,10 @@ public java.lang.String getName() { * * *
-     * The name of the session. This is always system-assigned; values provided
-     * when creating a session are ignored.
+     * Output only. The name of the session. This is always system-assigned.
      * 
* - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = OUTPUT_ONLY]; * * @return The bytes for name. */ @@ -898,11 +903,10 @@ public com.google.protobuf.ByteString getNameBytes() { * * *
-     * The name of the session. This is always system-assigned; values provided
-     * when creating a session are ignored.
+     * Output only. The name of the session. This is always system-assigned.
      * 
* - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = OUTPUT_ONLY]; * * @param value The name to set. * @return This builder for chaining. @@ -920,11 +924,10 @@ public Builder setName(java.lang.String value) { * * *
-     * The name of the session. This is always system-assigned; values provided
-     * when creating a session are ignored.
+     * Output only. The name of the session. This is always system-assigned.
      * 
* - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = OUTPUT_ONLY]; * * @return This builder for chaining. */ @@ -938,11 +941,10 @@ public Builder clearName() { * * *
-     * The name of the session. This is always system-assigned; values provided
-     * when creating a session are ignored.
+     * Output only. The name of the session. This is always system-assigned.
      * 
* - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = OUTPUT_ONLY]; * * @param value The bytes for name to set. * @return This builder for chaining. @@ -1170,7 +1172,9 @@ public Builder putAllLabels(java.util.Map va * Output only. The timestamp when the session is created. *
* - * .google.protobuf.Timestamp create_time = 3; + * + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * * * @return Whether the createTime field is set. */ @@ -1184,7 +1188,9 @@ public boolean hasCreateTime() { * Output only. The timestamp when the session is created. *
* - * .google.protobuf.Timestamp create_time = 3; + * + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * * * @return The createTime. */ @@ -1204,7 +1210,9 @@ public com.google.protobuf.Timestamp getCreateTime() { * Output only. The timestamp when the session is created. *
* - * .google.protobuf.Timestamp create_time = 3; + * + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ public Builder setCreateTime(com.google.protobuf.Timestamp value) { if (createTimeBuilder_ == null) { @@ -1226,7 +1234,9 @@ public Builder setCreateTime(com.google.protobuf.Timestamp value) { * Output only. The timestamp when the session is created. *
* - * .google.protobuf.Timestamp create_time = 3; + * + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ public Builder setCreateTime(com.google.protobuf.Timestamp.Builder builderForValue) { if (createTimeBuilder_ == null) { @@ -1245,7 +1255,9 @@ public Builder setCreateTime(com.google.protobuf.Timestamp.Builder builderForVal * Output only. The timestamp when the session is created. *
* - * .google.protobuf.Timestamp create_time = 3; + * + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ public Builder mergeCreateTime(com.google.protobuf.Timestamp value) { if (createTimeBuilder_ == null) { @@ -1269,7 +1281,9 @@ public Builder mergeCreateTime(com.google.protobuf.Timestamp value) { * Output only. The timestamp when the session is created. *
* - * .google.protobuf.Timestamp create_time = 3; + * + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ public Builder clearCreateTime() { if (createTimeBuilder_ == null) { @@ -1289,7 +1303,9 @@ public Builder clearCreateTime() { * Output only. The timestamp when the session is created. *
* - * .google.protobuf.Timestamp create_time = 3; + * + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ public com.google.protobuf.Timestamp.Builder getCreateTimeBuilder() { @@ -1303,7 +1319,9 @@ public com.google.protobuf.Timestamp.Builder getCreateTimeBuilder() { * Output only. The timestamp when the session is created. *
* - * .google.protobuf.Timestamp create_time = 3; + * + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ public com.google.protobuf.TimestampOrBuilder getCreateTimeOrBuilder() { if (createTimeBuilder_ != null) { @@ -1321,7 +1339,9 @@ public com.google.protobuf.TimestampOrBuilder getCreateTimeOrBuilder() { * Output only. The timestamp when the session is created. *
* - * .google.protobuf.Timestamp create_time = 3; + * + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ private com.google.protobuf.SingleFieldBuilderV3< com.google.protobuf.Timestamp, @@ -1354,7 +1374,9 @@ public com.google.protobuf.TimestampOrBuilder getCreateTimeOrBuilder() { * typically earlier than the actual last use time. * * - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * * * @return Whether the approximateLastUseTime field is set. */ @@ -1369,7 +1391,9 @@ public boolean hasApproximateLastUseTime() { * typically earlier than the actual last use time. * * - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * * * @return The approximateLastUseTime. */ @@ -1390,7 +1414,9 @@ public com.google.protobuf.Timestamp getApproximateLastUseTime() { * typically earlier than the actual last use time. * * - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ public Builder setApproximateLastUseTime(com.google.protobuf.Timestamp value) { if (approximateLastUseTimeBuilder_ == null) { @@ -1413,7 +1439,9 @@ public Builder setApproximateLastUseTime(com.google.protobuf.Timestamp value) { * typically earlier than the actual last use time. * * - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ public Builder setApproximateLastUseTime( com.google.protobuf.Timestamp.Builder builderForValue) { @@ -1434,7 +1462,9 @@ public Builder setApproximateLastUseTime( * typically earlier than the actual last use time. * * - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ public Builder mergeApproximateLastUseTime(com.google.protobuf.Timestamp value) { if (approximateLastUseTimeBuilder_ == null) { @@ -1461,7 +1491,9 @@ public Builder mergeApproximateLastUseTime(com.google.protobuf.Timestamp value) * typically earlier than the actual last use time. * * - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ public Builder clearApproximateLastUseTime() { if (approximateLastUseTimeBuilder_ == null) { @@ -1482,7 +1514,9 @@ public Builder clearApproximateLastUseTime() { * typically earlier than the actual last use time. * * - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ public com.google.protobuf.Timestamp.Builder getApproximateLastUseTimeBuilder() { @@ -1497,7 +1531,9 @@ public com.google.protobuf.Timestamp.Builder getApproximateLastUseTimeBuilder() * typically earlier than the actual last use time. * * - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ public com.google.protobuf.TimestampOrBuilder getApproximateLastUseTimeOrBuilder() { if (approximateLastUseTimeBuilder_ != null) { @@ -1516,7 +1552,9 @@ public com.google.protobuf.TimestampOrBuilder getApproximateLastUseTimeOrBuilder * typically earlier than the actual last use time. * * - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ private com.google.protobuf.SingleFieldBuilderV3< com.google.protobuf.Timestamp, diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SessionOrBuilder.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SessionOrBuilder.java index 6b7fc5b822a..556832d0093 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SessionOrBuilder.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SessionOrBuilder.java @@ -27,11 +27,10 @@ public interface SessionOrBuilder * * *
-   * The name of the session. This is always system-assigned; values provided
-   * when creating a session are ignored.
+   * Output only. The name of the session. This is always system-assigned.
    * 
* - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = OUTPUT_ONLY]; * * @return The name. */ @@ -40,11 +39,10 @@ public interface SessionOrBuilder * * *
-   * The name of the session. This is always system-assigned; values provided
-   * when creating a session are ignored.
+   * Output only. The name of the session. This is always system-assigned.
    * 
* - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = OUTPUT_ONLY]; * * @return The bytes for name. */ @@ -141,7 +139,8 @@ public interface SessionOrBuilder * Output only. The timestamp when the session is created. * * - * .google.protobuf.Timestamp create_time = 3; + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * * * @return Whether the createTime field is set. */ @@ -153,7 +152,8 @@ public interface SessionOrBuilder * Output only. The timestamp when the session is created. * * - * .google.protobuf.Timestamp create_time = 3; + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * * * @return The createTime. */ @@ -165,7 +165,8 @@ public interface SessionOrBuilder * Output only. The timestamp when the session is created. * * - * .google.protobuf.Timestamp create_time = 3; + * .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ com.google.protobuf.TimestampOrBuilder getCreateTimeOrBuilder(); @@ -177,7 +178,9 @@ public interface SessionOrBuilder * typically earlier than the actual last use time. * * - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * * * @return Whether the approximateLastUseTime field is set. */ @@ -190,7 +193,9 @@ public interface SessionOrBuilder * typically earlier than the actual last use time. * * - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * * * @return The approximateLastUseTime. */ @@ -203,7 +208,9 @@ public interface SessionOrBuilder * typically earlier than the actual last use time. * * - * .google.protobuf.Timestamp approximate_last_use_time = 4; + * + * .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * */ com.google.protobuf.TimestampOrBuilder getApproximateLastUseTimeOrBuilder(); } diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SpannerProto.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SpannerProto.java index 0932ab5d52a..8458063adac 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SpannerProto.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SpannerProto.java @@ -131,6 +131,10 @@ public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry r internal_static_google_spanner_v1_CommitResponse_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_google_spanner_v1_CommitResponse_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_google_spanner_v1_CommitResponse_CommitStats_descriptor; + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_google_spanner_v1_CommitResponse_CommitStats_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_google_spanner_v1_RollbackRequest_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable @@ -148,198 +152,202 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "e.spanner.v1\032\034google/api/annotations.pro" + "to\032\027google/api/client.proto\032\037google/api/" + "field_behavior.proto\032\031google/api/resourc" - + "e.proto\032\033google/protobuf/empty.proto\032\034go" - + "ogle/protobuf/struct.proto\032\037google/proto" - + "buf/timestamp.proto\032\027google/rpc/status.p" - + "roto\032\034google/spanner/v1/keys.proto\032 goog" - + "le/spanner/v1/mutation.proto\032\"google/spa" - + "nner/v1/result_set.proto\032#google/spanner" - + "/v1/transaction.proto\032\034google/spanner/v1" - + "/type.proto\"~\n\024CreateSessionRequest\0229\n\010d" - + "atabase\030\001 \001(\tB\'\340A\002\372A!\n\037spanner.googleapi" - + "s.com/Database\022+\n\007session\030\002 \001(\0132\032.google" - + ".spanner.v1.Session\"\251\001\n\032BatchCreateSessi" - + "onsRequest\0229\n\010database\030\001 \001(\tB\'\340A\002\372A!\n\037sp" - + "anner.googleapis.com/Database\0224\n\020session" - + "_template\030\002 \001(\0132\032.google.spanner.v1.Sess" - + "ion\022\032\n\rsession_count\030\003 \001(\005B\003\340A\002\"J\n\033Batch" - + "CreateSessionsResponse\022+\n\007session\030\001 \003(\0132" - + "\032.google.spanner.v1.Session\"\344\002\n\007Session\022" - + "\014\n\004name\030\001 \001(\t\0226\n\006labels\030\002 \003(\0132&.google.s" - + "panner.v1.Session.LabelsEntry\022/\n\013create_" - + "time\030\003 \001(\0132\032.google.protobuf.Timestamp\022=" - + "\n\031approximate_last_use_time\030\004 \001(\0132\032.goog" - + "le.protobuf.Timestamp\032-\n\013LabelsEntry\022\013\n\003" - + "key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001:t\352Aq\n\036spann" - + "er.googleapis.com/Session\022Oprojects/{pro" - + "ject}/instances/{instance}/databases/{da" - + "tabase}/sessions/{session}\"I\n\021GetSession" - + "Request\0224\n\004name\030\001 \001(\tB&\340A\002\372A \n\036spanner.g" - + "oogleapis.com/Session\"\207\001\n\023ListSessionsRe" - + "quest\0229\n\010database\030\001 \001(\tB\'\340A\002\372A!\n\037spanner" - + ".googleapis.com/Database\022\021\n\tpage_size\030\002 " - + "\001(\005\022\022\n\npage_token\030\003 \001(\t\022\016\n\006filter\030\004 \001(\t\"" - + "]\n\024ListSessionsResponse\022,\n\010sessions\030\001 \003(" - + "\0132\032.google.spanner.v1.Session\022\027\n\017next_pa" - + "ge_token\030\002 \001(\t\"L\n\024DeleteSessionRequest\0224" - + "\n\004name\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapis" - + ".com/Session\"\202\005\n\021ExecuteSqlRequest\0227\n\007se" - + "ssion\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapis." - + "com/Session\022;\n\013transaction\030\002 \001(\0132&.googl" - + "e.spanner.v1.TransactionSelector\022\020\n\003sql\030" - + "\003 \001(\tB\003\340A\002\022\'\n\006params\030\004 \001(\0132\027.google.prot" - + "obuf.Struct\022I\n\013param_types\030\005 \003(\01324.googl" - + "e.spanner.v1.ExecuteSqlRequest.ParamType" - + "sEntry\022\024\n\014resume_token\030\006 \001(\014\022B\n\nquery_mo" - + "de\030\007 \001(\0162..google.spanner.v1.ExecuteSqlR" - + "equest.QueryMode\022\027\n\017partition_token\030\010 \001(" - + "\014\022\r\n\005seqno\030\t \001(\003\022H\n\rquery_options\030\n \001(\0132" - + "1.google.spanner.v1.ExecuteSqlRequest.Qu" - + "eryOptions\032)\n\014QueryOptions\022\031\n\021optimizer_" - + "version\030\001 \001(\t\032J\n\017ParamTypesEntry\022\013\n\003key\030" - + "\001 \001(\t\022&\n\005value\030\002 \001(\0132\027.google.spanner.v1" - + ".Type:\0028\001\".\n\tQueryMode\022\n\n\006NORMAL\020\000\022\010\n\004PL" - + "AN\020\001\022\013\n\007PROFILE\020\002\"\337\003\n\026ExecuteBatchDmlReq" - + "uest\0227\n\007session\030\001 \001(\tB&\340A\002\372A \n\036spanner.g" - + "oogleapis.com/Session\022@\n\013transaction\030\002 \001" - + "(\0132&.google.spanner.v1.TransactionSelect" - + "orB\003\340A\002\022L\n\nstatements\030\003 \003(\01323.google.spa" - + "nner.v1.ExecuteBatchDmlRequest.Statement" - + "B\003\340A\002\022\022\n\005seqno\030\004 \001(\003B\003\340A\002\032\347\001\n\tStatement\022" - + "\013\n\003sql\030\001 \001(\t\022\'\n\006params\030\002 \001(\0132\027.google.pr" - + "otobuf.Struct\022X\n\013param_types\030\003 \003(\0132C.goo" + + "e.proto\032\036google/protobuf/duration.proto\032" + + "\033google/protobuf/empty.proto\032\034google/pro" + + "tobuf/struct.proto\032\037google/protobuf/time" + + "stamp.proto\032\027google/rpc/status.proto\032\034go" + + "ogle/spanner/v1/keys.proto\032 google/spann" + + "er/v1/mutation.proto\032\"google/spanner/v1/" + + "result_set.proto\032#google/spanner/v1/tran" + + "saction.proto\032\034google/spanner/v1/type.pr" + + "oto\"~\n\024CreateSessionRequest\0229\n\010database\030" + + "\001 \001(\tB\'\340A\002\372A!\n\037spanner.googleapis.com/Da" + + "tabase\022+\n\007session\030\002 \001(\0132\032.google.spanner" + + ".v1.Session\"\251\001\n\032BatchCreateSessionsReque" + + "st\0229\n\010database\030\001 \001(\tB\'\340A\002\372A!\n\037spanner.go" + + "ogleapis.com/Database\0224\n\020session_templat" + + "e\030\002 \001(\0132\032.google.spanner.v1.Session\022\032\n\rs" + + "ession_count\030\003 \001(\005B\003\340A\002\"J\n\033BatchCreateSe" + + "ssionsResponse\022+\n\007session\030\001 \003(\0132\032.google" + + ".spanner.v1.Session\"\363\002\n\007Session\022\021\n\004name\030" + + "\001 \001(\tB\003\340A\003\0226\n\006labels\030\002 \003(\0132&.google.span" + + "ner.v1.Session.LabelsEntry\0224\n\013create_tim" + + "e\030\003 \001(\0132\032.google.protobuf.TimestampB\003\340A\003" + + "\022B\n\031approximate_last_use_time\030\004 \001(\0132\032.go" + + "ogle.protobuf.TimestampB\003\340A\003\032-\n\013LabelsEn" + + "try\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001:t\352Aq" + + "\n\036spanner.googleapis.com/Session\022Oprojec" + + "ts/{project}/instances/{instance}/databa" + + "ses/{database}/sessions/{session}\"I\n\021Get" + + "SessionRequest\0224\n\004name\030\001 \001(\tB&\340A\002\372A \n\036sp" + + "anner.googleapis.com/Session\"\207\001\n\023ListSes" + + "sionsRequest\0229\n\010database\030\001 \001(\tB\'\340A\002\372A!\n\037" + + "spanner.googleapis.com/Database\022\021\n\tpage_" + + "size\030\002 \001(\005\022\022\n\npage_token\030\003 \001(\t\022\016\n\006filter" + + "\030\004 \001(\t\"]\n\024ListSessionsResponse\022,\n\010sessio" + + "ns\030\001 \003(\0132\032.google.spanner.v1.Session\022\027\n\017" + + "next_page_token\030\002 \001(\t\"L\n\024DeleteSessionRe" + + "quest\0224\n\004name\030\001 \001(\tB&\340A\002\372A \n\036spanner.goo" + + "gleapis.com/Session\"\202\005\n\021ExecuteSqlReques" + + "t\0227\n\007session\030\001 \001(\tB&\340A\002\372A \n\036spanner.goog" + + "leapis.com/Session\022;\n\013transaction\030\002 \001(\0132" + + "&.google.spanner.v1.TransactionSelector\022" + + "\020\n\003sql\030\003 \001(\tB\003\340A\002\022\'\n\006params\030\004 \001(\0132\027.goog" + + "le.protobuf.Struct\022I\n\013param_types\030\005 \003(\0132" + + "4.google.spanner.v1.ExecuteSqlRequest.Pa" + + "ramTypesEntry\022\024\n\014resume_token\030\006 \001(\014\022B\n\nq" + + "uery_mode\030\007 \001(\0162..google.spanner.v1.Exec" + + "uteSqlRequest.QueryMode\022\027\n\017partition_tok" + + "en\030\010 \001(\014\022\r\n\005seqno\030\t \001(\003\022H\n\rquery_options" + + "\030\n \001(\01321.google.spanner.v1.ExecuteSqlReq" + + "uest.QueryOptions\032)\n\014QueryOptions\022\031\n\021opt" + + "imizer_version\030\001 \001(\t\032J\n\017ParamTypesEntry\022" + + "\013\n\003key\030\001 \001(\t\022&\n\005value\030\002 \001(\0132\027.google.spa" + + "nner.v1.Type:\0028\001\".\n\tQueryMode\022\n\n\006NORMAL\020" + + "\000\022\010\n\004PLAN\020\001\022\013\n\007PROFILE\020\002\"\337\003\n\026ExecuteBatc" + + "hDmlRequest\0227\n\007session\030\001 \001(\tB&\340A\002\372A \n\036sp" + + "anner.googleapis.com/Session\022@\n\013transact" + + "ion\030\002 \001(\0132&.google.spanner.v1.Transactio" + + "nSelectorB\003\340A\002\022L\n\nstatements\030\003 \003(\01323.goo" + "gle.spanner.v1.ExecuteBatchDmlRequest.St" - + "atement.ParamTypesEntry\032J\n\017ParamTypesEnt" - + "ry\022\013\n\003key\030\001 \001(\t\022&\n\005value\030\002 \001(\0132\027.google." - + "spanner.v1.Type:\0028\001\"p\n\027ExecuteBatchDmlRe" - + "sponse\0221\n\013result_sets\030\001 \003(\0132\034.google.spa" - + "nner.v1.ResultSet\022\"\n\006status\030\002 \001(\0132\022.goog" - + "le.rpc.Status\"H\n\020PartitionOptions\022\034\n\024par" - + "tition_size_bytes\030\001 \001(\003\022\026\n\016max_partition" - + "s\030\002 \001(\003\"\243\003\n\025PartitionQueryRequest\0227\n\007ses" - + "sion\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapis.c" - + "om/Session\022;\n\013transaction\030\002 \001(\0132&.google" - + ".spanner.v1.TransactionSelector\022\020\n\003sql\030\003" - + " \001(\tB\003\340A\002\022\'\n\006params\030\004 \001(\0132\027.google.proto" - + "buf.Struct\022M\n\013param_types\030\005 \003(\01328.google" - + ".spanner.v1.PartitionQueryRequest.ParamT" - + "ypesEntry\022>\n\021partition_options\030\006 \001(\0132#.g" - + "oogle.spanner.v1.PartitionOptions\032J\n\017Par" - + "amTypesEntry\022\013\n\003key\030\001 \001(\t\022&\n\005value\030\002 \001(\013" - + "2\027.google.spanner.v1.Type:\0028\001\"\261\002\n\024Partit" - + "ionReadRequest\0227\n\007session\030\001 \001(\tB&\340A\002\372A \n" - + "\036spanner.googleapis.com/Session\022;\n\013trans" - + "action\030\002 \001(\0132&.google.spanner.v1.Transac" - + "tionSelector\022\022\n\005table\030\003 \001(\tB\003\340A\002\022\r\n\005inde" - + "x\030\004 \001(\t\022\017\n\007columns\030\005 \003(\t\022/\n\007key_set\030\006 \001(" - + "\0132\031.google.spanner.v1.KeySetB\003\340A\002\022>\n\021par" - + "tition_options\030\t \001(\0132#.google.spanner.v1" - + ".PartitionOptions\"$\n\tPartition\022\027\n\017partit" - + "ion_token\030\001 \001(\014\"z\n\021PartitionResponse\0220\n\n" - + "partitions\030\001 \003(\0132\034.google.spanner.v1.Par" - + "tition\0223\n\013transaction\030\002 \001(\0132\036.google.spa" - + "nner.v1.Transaction\"\253\002\n\013ReadRequest\0227\n\007s" - + "ession\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapis" - + ".com/Session\022;\n\013transaction\030\002 \001(\0132&.goog" - + "le.spanner.v1.TransactionSelector\022\022\n\005tab" - + "le\030\003 \001(\tB\003\340A\002\022\r\n\005index\030\004 \001(\t\022\024\n\007columns\030" - + "\005 \003(\tB\003\340A\002\022/\n\007key_set\030\006 \001(\0132\031.google.spa" - + "nner.v1.KeySetB\003\340A\002\022\r\n\005limit\030\010 \001(\003\022\024\n\014re" - + "sume_token\030\t \001(\014\022\027\n\017partition_token\030\n \001(" - + "\014\"\217\001\n\027BeginTransactionRequest\0227\n\007session" - + "\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapis.com/S" - + "ession\022;\n\007options\030\002 \001(\0132%.google.spanner" - + ".v1.TransactionOptionsB\003\340A\002\"\352\001\n\rCommitRe" - + "quest\0227\n\007session\030\001 \001(\tB&\340A\002\372A \n\036spanner." - + "googleapis.com/Session\022\030\n\016transaction_id" - + "\030\002 \001(\014H\000\022G\n\026single_use_transaction\030\003 \001(\013" - + "2%.google.spanner.v1.TransactionOptionsH" - + "\000\022.\n\tmutations\030\004 \003(\0132\033.google.spanner.v1" - + ".MutationB\r\n\013transaction\"F\n\016CommitRespon" - + "se\0224\n\020commit_timestamp\030\001 \001(\0132\032.google.pr" - + "otobuf.Timestamp\"g\n\017RollbackRequest\0227\n\007s" - + "ession\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapis" - + ".com/Session\022\033\n\016transaction_id\030\002 \001(\014B\003\340A" - + "\0022\300\026\n\007Spanner\022\246\001\n\rCreateSession\022\'.google" - + ".spanner.v1.CreateSessionRequest\032\032.googl" - + "e.spanner.v1.Session\"P\202\323\344\223\002?\":/v1/{datab" - + "ase=projects/*/instances/*/databases/*}/" - + "sessions:\001*\332A\010database\022\340\001\n\023BatchCreateSe" - + "ssions\022-.google.spanner.v1.BatchCreateSe" - + "ssionsRequest\032..google.spanner.v1.BatchC" - + "reateSessionsResponse\"j\202\323\344\223\002K\"F/v1/{data" - + "base=projects/*/instances/*/databases/*}" - + "/sessions:batchCreate:\001*\332A\026database,sess" - + "ion_count\022\227\001\n\nGetSession\022$.google.spanne" - + "r.v1.GetSessionRequest\032\032.google.spanner." - + "v1.Session\"G\202\323\344\223\002:\0228/v1/{name=projects/*" - + "/instances/*/databases/*/sessions/*}\332A\004n" - + "ame\022\256\001\n\014ListSessions\022&.google.spanner.v1" - + ".ListSessionsRequest\032\'.google.spanner.v1" - + ".ListSessionsResponse\"M\202\323\344\223\002<\022:/v1/{data" - + "base=projects/*/instances/*/databases/*}" - + "/sessions\332A\010database\022\231\001\n\rDeleteSession\022\'" - + ".google.spanner.v1.DeleteSessionRequest\032" - + "\026.google.protobuf.Empty\"G\202\323\344\223\002:*8/v1/{na" - + "me=projects/*/instances/*/databases/*/se" - + "ssions/*}\332A\004name\022\243\001\n\nExecuteSql\022$.google" - + ".spanner.v1.ExecuteSqlRequest\032\034.google.s" - + "panner.v1.ResultSet\"Q\202\323\344\223\002K\"F/v1/{sessio" - + "n=projects/*/instances/*/databases/*/ses" - + "sions/*}:executeSql:\001*\022\276\001\n\023ExecuteStream" - + "ingSql\022$.google.spanner.v1.ExecuteSqlReq" - + "uest\032#.google.spanner.v1.PartialResultSe" - + "t\"Z\202\323\344\223\002T\"O/v1/{session=projects/*/insta" - + "nces/*/databases/*/sessions/*}:executeSt" - + "reamingSql:\001*0\001\022\300\001\n\017ExecuteBatchDml\022).go" - + "ogle.spanner.v1.ExecuteBatchDmlRequest\032*" - + ".google.spanner.v1.ExecuteBatchDmlRespon" - + "se\"V\202\323\344\223\002P\"K/v1/{session=projects/*/inst" - + "ances/*/databases/*/sessions/*}:executeB" - + "atchDml:\001*\022\221\001\n\004Read\022\036.google.spanner.v1." - + "ReadRequest\032\034.google.spanner.v1.ResultSe" - + "t\"K\202\323\344\223\002E\"@/v1/{session=projects/*/insta" - + "nces/*/databases/*/sessions/*}:read:\001*\022\254" - + "\001\n\rStreamingRead\022\036.google.spanner.v1.Rea" - + "dRequest\032#.google.spanner.v1.PartialResu" - + "ltSet\"T\202\323\344\223\002N\"I/v1/{session=projects/*/i" - + "nstances/*/databases/*/sessions/*}:strea" - + "mingRead:\001*0\001\022\311\001\n\020BeginTransaction\022*.goo" - + "gle.spanner.v1.BeginTransactionRequest\032\036" - + ".google.spanner.v1.Transaction\"i\202\323\344\223\002Q\"L" + + "atementB\003\340A\002\022\022\n\005seqno\030\004 \001(\003B\003\340A\002\032\347\001\n\tSta" + + "tement\022\013\n\003sql\030\001 \001(\t\022\'\n\006params\030\002 \001(\0132\027.go" + + "ogle.protobuf.Struct\022X\n\013param_types\030\003 \003(" + + "\0132C.google.spanner.v1.ExecuteBatchDmlReq" + + "uest.Statement.ParamTypesEntry\032J\n\017ParamT" + + "ypesEntry\022\013\n\003key\030\001 \001(\t\022&\n\005value\030\002 \001(\0132\027." + + "google.spanner.v1.Type:\0028\001\"p\n\027ExecuteBat" + + "chDmlResponse\0221\n\013result_sets\030\001 \003(\0132\034.goo" + + "gle.spanner.v1.ResultSet\022\"\n\006status\030\002 \001(\013" + + "2\022.google.rpc.Status\"H\n\020PartitionOptions" + + "\022\034\n\024partition_size_bytes\030\001 \001(\003\022\026\n\016max_pa" + + "rtitions\030\002 \001(\003\"\243\003\n\025PartitionQueryRequest" + + "\0227\n\007session\030\001 \001(\tB&\340A\002\372A \n\036spanner.googl" + + "eapis.com/Session\022;\n\013transaction\030\002 \001(\0132&" + + ".google.spanner.v1.TransactionSelector\022\020" + + "\n\003sql\030\003 \001(\tB\003\340A\002\022\'\n\006params\030\004 \001(\0132\027.googl" + + "e.protobuf.Struct\022M\n\013param_types\030\005 \003(\01328" + + ".google.spanner.v1.PartitionQueryRequest" + + ".ParamTypesEntry\022>\n\021partition_options\030\006 " + + "\001(\0132#.google.spanner.v1.PartitionOptions" + + "\032J\n\017ParamTypesEntry\022\013\n\003key\030\001 \001(\t\022&\n\005valu" + + "e\030\002 \001(\0132\027.google.spanner.v1.Type:\0028\001\"\261\002\n" + + "\024PartitionReadRequest\0227\n\007session\030\001 \001(\tB&" + + "\340A\002\372A \n\036spanner.googleapis.com/Session\022;" + + "\n\013transaction\030\002 \001(\0132&.google.spanner.v1." + + "TransactionSelector\022\022\n\005table\030\003 \001(\tB\003\340A\002\022" + + "\r\n\005index\030\004 \001(\t\022\017\n\007columns\030\005 \003(\t\022/\n\007key_s" + + "et\030\006 \001(\0132\031.google.spanner.v1.KeySetB\003\340A\002" + + "\022>\n\021partition_options\030\t \001(\0132#.google.spa" + + "nner.v1.PartitionOptions\"$\n\tPartition\022\027\n" + + "\017partition_token\030\001 \001(\014\"z\n\021PartitionRespo" + + "nse\0220\n\npartitions\030\001 \003(\0132\034.google.spanner" + + ".v1.Partition\0223\n\013transaction\030\002 \001(\0132\036.goo" + + "gle.spanner.v1.Transaction\"\253\002\n\013ReadReque" + + "st\0227\n\007session\030\001 \001(\tB&\340A\002\372A \n\036spanner.goo" + + "gleapis.com/Session\022;\n\013transaction\030\002 \001(\013" + + "2&.google.spanner.v1.TransactionSelector" + + "\022\022\n\005table\030\003 \001(\tB\003\340A\002\022\r\n\005index\030\004 \001(\t\022\024\n\007c" + + "olumns\030\005 \003(\tB\003\340A\002\022/\n\007key_set\030\006 \001(\0132\031.goo" + + "gle.spanner.v1.KeySetB\003\340A\002\022\r\n\005limit\030\010 \001(" + + "\003\022\024\n\014resume_token\030\t \001(\014\022\027\n\017partition_tok" + + "en\030\n \001(\014\"\217\001\n\027BeginTransactionRequest\0227\n\007" + + "session\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapi" + + "s.com/Session\022;\n\007options\030\002 \001(\0132%.google." + + "spanner.v1.TransactionOptionsB\003\340A\002\"\207\002\n\rC" + + "ommitRequest\0227\n\007session\030\001 \001(\tB&\340A\002\372A \n\036s" + + "panner.googleapis.com/Session\022\030\n\016transac" + + "tion_id\030\002 \001(\014H\000\022G\n\026single_use_transactio" + + "n\030\003 \001(\0132%.google.spanner.v1.TransactionO" + + "ptionsH\000\022.\n\tmutations\030\004 \003(\0132\033.google.spa" + + "nner.v1.Mutation\022\033\n\023return_commit_stats\030" + + "\005 \001(\010B\r\n\013transaction\"\262\001\n\016CommitResponse\022" + + "4\n\020commit_timestamp\030\001 \001(\0132\032.google.proto" + + "buf.Timestamp\022C\n\014commit_stats\030\002 \001(\0132-.go" + + "ogle.spanner.v1.CommitResponse.CommitSta" + + "ts\032%\n\013CommitStats\022\026\n\016mutation_count\030\001 \001(" + + "\003\"g\n\017RollbackRequest\0227\n\007session\030\001 \001(\tB&\340" + + "A\002\372A \n\036spanner.googleapis.com/Session\022\033\n" + + "\016transaction_id\030\002 \001(\014B\003\340A\0022\300\026\n\007Spanner\022\246" + + "\001\n\rCreateSession\022\'.google.spanner.v1.Cre" + + "ateSessionRequest\032\032.google.spanner.v1.Se" + + "ssion\"P\202\323\344\223\002?\":/v1/{database=projects/*/" + + "instances/*/databases/*}/sessions:\001*\332A\010d" + + "atabase\022\340\001\n\023BatchCreateSessions\022-.google" + + ".spanner.v1.BatchCreateSessionsRequest\032." + + ".google.spanner.v1.BatchCreateSessionsRe" + + "sponse\"j\202\323\344\223\002K\"F/v1/{database=projects/*" + + "/instances/*/databases/*}/sessions:batch" + + "Create:\001*\332A\026database,session_count\022\227\001\n\nG" + + "etSession\022$.google.spanner.v1.GetSession" + + "Request\032\032.google.spanner.v1.Session\"G\202\323\344" + + "\223\002:\0228/v1/{name=projects/*/instances/*/da" + + "tabases/*/sessions/*}\332A\004name\022\256\001\n\014ListSes" + + "sions\022&.google.spanner.v1.ListSessionsRe" + + "quest\032\'.google.spanner.v1.ListSessionsRe" + + "sponse\"M\202\323\344\223\002<\022:/v1/{database=projects/*" + + "/instances/*/databases/*}/sessions\332A\010dat" + + "abase\022\231\001\n\rDeleteSession\022\'.google.spanner" + + ".v1.DeleteSessionRequest\032\026.google.protob" + + "uf.Empty\"G\202\323\344\223\002:*8/v1/{name=projects/*/i" + + "nstances/*/databases/*/sessions/*}\332A\004nam" + + "e\022\243\001\n\nExecuteSql\022$.google.spanner.v1.Exe" + + "cuteSqlRequest\032\034.google.spanner.v1.Resul" + + "tSet\"Q\202\323\344\223\002K\"F/v1/{session=projects/*/in" + + "stances/*/databases/*/sessions/*}:execut" + + "eSql:\001*\022\276\001\n\023ExecuteStreamingSql\022$.google" + + ".spanner.v1.ExecuteSqlRequest\032#.google.s" + + "panner.v1.PartialResultSet\"Z\202\323\344\223\002T\"O/v1/" + + "{session=projects/*/instances/*/database" + + "s/*/sessions/*}:executeStreamingSql:\001*0\001" + + "\022\300\001\n\017ExecuteBatchDml\022).google.spanner.v1" + + ".ExecuteBatchDmlRequest\032*.google.spanner" + + ".v1.ExecuteBatchDmlResponse\"V\202\323\344\223\002P\"K/v1" + + "/{session=projects/*/instances/*/databas" + + "es/*/sessions/*}:executeBatchDml:\001*\022\221\001\n\004" + + "Read\022\036.google.spanner.v1.ReadRequest\032\034.g" + + "oogle.spanner.v1.ResultSet\"K\202\323\344\223\002E\"@/v1/" + + "{session=projects/*/instances/*/database" + + "s/*/sessions/*}:read:\001*\022\254\001\n\rStreamingRea" + + "d\022\036.google.spanner.v1.ReadRequest\032#.goog" + + "le.spanner.v1.PartialResultSet\"T\202\323\344\223\002N\"I" + "/v1/{session=projects/*/instances/*/data" - + "bases/*/sessions/*}:beginTransaction:\001*\332" - + "A\017session,options\022\353\001\n\006Commit\022 .google.sp" - + "anner.v1.CommitRequest\032!.google.spanner." - + "v1.CommitResponse\"\233\001\202\323\344\223\002G\"B/v1/{session" - + "=projects/*/instances/*/databases/*/sess" - + "ions/*}:commit:\001*\332A session,transaction_" - + "id,mutations\332A(session,single_use_transa" - + "ction,mutations\022\260\001\n\010Rollback\022\".google.sp" - + "anner.v1.RollbackRequest\032\026.google.protob" - + "uf.Empty\"h\202\323\344\223\002I\"D/v1/{session=projects/" - + "*/instances/*/databases/*/sessions/*}:ro" - + "llback:\001*\332A\026session,transaction_id\022\267\001\n\016P" - + "artitionQuery\022(.google.spanner.v1.Partit" - + "ionQueryRequest\032$.google.spanner.v1.Part" - + "itionResponse\"U\202\323\344\223\002O\"J/v1/{session=proj" - + "ects/*/instances/*/databases/*/sessions/" - + "*}:partitionQuery:\001*\022\264\001\n\rPartitionRead\022\'" - + ".google.spanner.v1.PartitionReadRequest\032" - + "$.google.spanner.v1.PartitionResponse\"T\202" - + "\323\344\223\002N\"I/v1/{session=projects/*/instances" - + "/*/databases/*/sessions/*}:partitionRead" - + ":\001*\032w\312A\026spanner.googleapis.com\322A[https:/" - + "/www.googleapis.com/auth/cloud-platform," - + "https://www.googleapis.com/auth/spanner." - + "dataB\224\002\n\025com.google.spanner.v1B\014SpannerP" - + "rotoP\001Z8google.golang.org/genproto/googl" - + "eapis/spanner/v1;spanner\252\002\027Google.Cloud." - + "Spanner.V1\312\002\027Google\\Cloud\\Spanner\\V1\352\002\032G" - + "oogle::Cloud::Spanner::V1\352A_\n\037spanner.go" - + "ogleapis.com/Database\022 @@ -148,7 +148,7 @@ public interface FieldOrBuilder * SQL queries, it is the column alias (e.g., `"Word"` in the * query `"SELECT 'hello' AS Word"`), or the column name (e.g., * `"ColName"` in the query `"SELECT ColName FROM Table"`). Some - * columns might have an empty name (e.g., !"SELECT + * columns might have an empty name (e.g., `"SELECT * UPPER(ColName)"`). Note that a query result can contain * multiple fields with the same name. * @@ -311,7 +311,7 @@ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { * SQL queries, it is the column alias (e.g., `"Word"` in the * query `"SELECT 'hello' AS Word"`), or the column name (e.g., * `"ColName"` in the query `"SELECT ColName FROM Table"`). Some - * columns might have an empty name (e.g., !"SELECT + * columns might have an empty name (e.g., `"SELECT * UPPER(ColName)"`). Note that a query result can contain * multiple fields with the same name. * @@ -340,7 +340,7 @@ public java.lang.String getName() { * SQL queries, it is the column alias (e.g., `"Word"` in the * query `"SELECT 'hello' AS Word"`), or the column name (e.g., * `"ColName"` in the query `"SELECT ColName FROM Table"`). Some - * columns might have an empty name (e.g., !"SELECT + * columns might have an empty name (e.g., `"SELECT * UPPER(ColName)"`). Note that a query result can contain * multiple fields with the same name. * @@ -764,7 +764,7 @@ public Builder mergeFrom( * SQL queries, it is the column alias (e.g., `"Word"` in the * query `"SELECT 'hello' AS Word"`), or the column name (e.g., * `"ColName"` in the query `"SELECT ColName FROM Table"`). Some - * columns might have an empty name (e.g., !"SELECT + * columns might have an empty name (e.g., `"SELECT * UPPER(ColName)"`). Note that a query result can contain * multiple fields with the same name. * @@ -792,7 +792,7 @@ public java.lang.String getName() { * SQL queries, it is the column alias (e.g., `"Word"` in the * query `"SELECT 'hello' AS Word"`), or the column name (e.g., * `"ColName"` in the query `"SELECT ColName FROM Table"`). Some - * columns might have an empty name (e.g., !"SELECT + * columns might have an empty name (e.g., `"SELECT * UPPER(ColName)"`). Note that a query result can contain * multiple fields with the same name. * @@ -820,7 +820,7 @@ public com.google.protobuf.ByteString getNameBytes() { * SQL queries, it is the column alias (e.g., `"Word"` in the * query `"SELECT 'hello' AS Word"`), or the column name (e.g., * `"ColName"` in the query `"SELECT ColName FROM Table"`). Some - * columns might have an empty name (e.g., !"SELECT + * columns might have an empty name (e.g., `"SELECT * UPPER(ColName)"`). Note that a query result can contain * multiple fields with the same name. * @@ -847,7 +847,7 @@ public Builder setName(java.lang.String value) { * SQL queries, it is the column alias (e.g., `"Word"` in the * query `"SELECT 'hello' AS Word"`), or the column name (e.g., * `"ColName"` in the query `"SELECT ColName FROM Table"`). Some - * columns might have an empty name (e.g., !"SELECT + * columns might have an empty name (e.g., `"SELECT * UPPER(ColName)"`). Note that a query result can contain * multiple fields with the same name. * @@ -870,7 +870,7 @@ public Builder clearName() { * SQL queries, it is the column alias (e.g., `"Word"` in the * query `"SELECT 'hello' AS Word"`), or the column name (e.g., * `"ColName"` in the query `"SELECT ColName FROM Table"`). Some - * columns might have an empty name (e.g., !"SELECT + * columns might have an empty name (e.g., `"SELECT * UPPER(ColName)"`). Note that a query result can contain * multiple fields with the same name. * diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TransactionOptions.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TransactionOptions.java index 93de3f1b96a..980f749f00a 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TransactionOptions.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TransactionOptions.java @@ -23,10 +23,11 @@ * *
  * # Transactions
- * Each session can have at most one active transaction at a time. After the
- * active transaction is completed, the session can immediately be
- * re-used for the next transaction. It is not necessary to create a
- * new session for each transaction.
+ * Each session can have at most one active transaction at a time (note that
+ * standalone reads and queries use a transaction internally and do count
+ * towards the one transaction limit). After the active transaction is
+ * completed, the session can immediately be re-used for the next transaction.
+ * It is not necessary to create a new session for each transaction.
  * # Transaction Modes
  * Cloud Spanner supports three transaction modes:
  *   1. Locking read-write. This type of transaction is the only way
@@ -4312,10 +4313,11 @@ protected Builder newBuilderForType(com.google.protobuf.GeneratedMessageV3.Build
    *
    * 
    * # Transactions
-   * Each session can have at most one active transaction at a time. After the
-   * active transaction is completed, the session can immediately be
-   * re-used for the next transaction. It is not necessary to create a
-   * new session for each transaction.
+   * Each session can have at most one active transaction at a time (note that
+   * standalone reads and queries use a transaction internally and do count
+   * towards the one transaction limit). After the active transaction is
+   * completed, the session can immediately be re-used for the next transaction.
+   * It is not necessary to create a new session for each transaction.
    * # Transaction Modes
    * Cloud Spanner supports three transaction modes:
    *   1. Locking read-write. This type of transaction is the only way
diff --git a/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/spanner.proto b/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/spanner.proto
index 93e4987ed16..8f579e333d3 100644
--- a/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/spanner.proto
+++ b/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/spanner.proto
@@ -20,6 +20,7 @@ import "google/api/annotations.proto";
 import "google/api/client.proto";
 import "google/api/field_behavior.proto";
 import "google/api/resource.proto";
+import "google/protobuf/duration.proto";
 import "google/protobuf/empty.proto";
 import "google/protobuf/struct.proto";
 import "google/protobuf/timestamp.proto";
@@ -219,6 +220,12 @@ service Spanner {
   // transactions. However, it can also happen for a variety of other
   // reasons. If `Commit` returns `ABORTED`, the caller should re-attempt
   // the transaction from the beginning, re-using the same session.
+  //
+  // On very rare occasions, `Commit` might return `UNKNOWN`. This can happen,
+  // for example, if the client job experiences a 1+ hour networking failure.
+  // At that point, Cloud Spanner has lost track of the transaction outcome and
+  // we recommend that you perform another read from the database to see the
+  // state of things as they are now.
   rpc Commit(CommitRequest) returns (CommitResponse) {
     option (google.api.http) = {
       post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:commit"
@@ -331,9 +338,8 @@ message Session {
     pattern: "projects/{project}/instances/{instance}/databases/{database}/sessions/{session}"
   };
 
-  // The name of the session. This is always system-assigned; values provided
-  // when creating a session are ignored.
-  string name = 1;
+  // Output only. The name of the session. This is always system-assigned.
+  string name = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
 
   // The labels for the session.
   //
@@ -347,11 +353,11 @@ message Session {
   map labels = 2;
 
   // Output only. The timestamp when the session is created.
-  google.protobuf.Timestamp create_time = 3;
+  google.protobuf.Timestamp create_time = 3 [(google.api.field_behavior) = OUTPUT_ONLY];
 
   // Output only. The approximate timestamp when the session is last used. It is
   // typically earlier than the actual last use time.
-  google.protobuf.Timestamp approximate_last_use_time = 4;
+  google.protobuf.Timestamp approximate_last_use_time = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
 }
 
 // The request for [GetSession][google.spanner.v1.Spanner.GetSession].
@@ -438,6 +444,9 @@ message ExecuteSqlRequest {
     // SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS. Executing a SQL statement
     // with an invalid optimizer version will fail with a syntax error
     // (`INVALID_ARGUMENT`) status.
+    // See
+    // https://cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer
+    // for more information on managing the query optimizer.
     //
     // The `optimizer_version` statement hint has precedence over this setting.
     string optimizer_version = 1;
@@ -483,8 +492,9 @@ message ExecuteSqlRequest {
   // Parameter names and values that bind to placeholders in the SQL string.
   //
   // A parameter placeholder consists of the `@` character followed by the
-  // parameter name (for example, `@firstName`). Parameter names can contain
-  // letters, numbers, and underscores.
+  // parameter name (for example, `@firstName`). Parameter names must conform
+  // to the naming requirements of identifiers as specified at
+  // https://cloud.google.com/spanner/docs/lexical#identifiers.
   //
   // Parameters can appear anywhere that a literal value is expected.  The same
   // parameter name can be used more than once, for example:
@@ -884,12 +894,34 @@ message CommitRequest {
   // mutations are applied atomically, in the order they appear in
   // this list.
   repeated Mutation mutations = 4;
+
+  // If `true`, then statistics related to the transaction will be included in
+  // the [CommitResponse][google.spanner.v1.CommitResponse.commit_stats]. Default value is
+  // `false`.
+  bool return_commit_stats = 5;
 }
 
 // The response for [Commit][google.spanner.v1.Spanner.Commit].
 message CommitResponse {
+  // Additional statistics about a commit.
+  message CommitStats {
+    // The total number of mutations for the transaction. Knowing the
+    // `mutation_count` value can help you maximize the number of mutations
+    // in a transaction and minimize the number of API round trips. You can
+    // also monitor this value to prevent transactions from exceeding the system
+    // [limit](http://cloud.google.com/spanner/quotas#limits_for_creating_reading_updating_and_deleting_data).
+    // If the number of mutations exceeds the limit, the server returns
+    // [INVALID_ARGUMENT](http://cloud.google.com/spanner/docs/reference/rest/v1/Code#ENUM_VALUES.INVALID_ARGUMENT).
+    int64 mutation_count = 1;
+  }
+
   // The Cloud Spanner timestamp at which the transaction committed.
   google.protobuf.Timestamp commit_timestamp = 1;
+
+  // The statistics about this Commit. Not returned by default.
+  // For more information, see
+  // [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+  CommitStats commit_stats = 2;
 }
 
 // The request for [Rollback][google.spanner.v1.Spanner.Rollback].
diff --git a/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/transaction.proto b/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/transaction.proto
index 0bcbfcf9002..2cafefcb107 100644
--- a/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/transaction.proto
+++ b/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/transaction.proto
@@ -31,10 +31,11 @@ option ruby_package = "Google::Cloud::Spanner::V1";
 // # Transactions
 //
 //
-// Each session can have at most one active transaction at a time. After the
-// active transaction is completed, the session can immediately be
-// re-used for the next transaction. It is not necessary to create a
-// new session for each transaction.
+// Each session can have at most one active transaction at a time (note that
+// standalone reads and queries use a transaction internally and do count
+// towards the one transaction limit). After the active transaction is
+// completed, the session can immediately be re-used for the next transaction.
+// It is not necessary to create a new session for each transaction.
 //
 // # Transaction Modes
 //
diff --git a/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/type.proto b/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/type.proto
index 1e5e5ff313a..1b863c0fdf4 100644
--- a/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/type.proto
+++ b/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/type.proto
@@ -50,7 +50,7 @@ message StructType {
     // SQL queries, it is the column alias (e.g., `"Word"` in the
     // query `"SELECT 'hello' AS Word"`), or the column name (e.g.,
     // `"ColName"` in the query `"SELECT ColName FROM Table"`). Some
-    // columns might have an empty name (e.g., !"SELECT
+    // columns might have an empty name (e.g., `"SELECT
     // UPPER(ColName)"`). Note that a query result can contain
     // multiple fields with the same name.
     string name = 1;
diff --git a/synth.metadata b/synth.metadata
index 0a947958495..554bb0a42ca 100644
--- a/synth.metadata
+++ b/synth.metadata
@@ -4,15 +4,15 @@
       "git": {
         "name": ".",
         "remote": "https://github.com/googleapis/java-spanner.git",
-        "sha": "15052653993a3924037b2c4ba27e04f553493885"
+        "sha": "4aa770ce669404ad0eca542ae5bd32a421748c18"
       }
     },
     {
       "git": {
         "name": "googleapis",
         "remote": "https://github.com/googleapis/googleapis.git",
-        "sha": "91e206bcfeaf8948ea03fe3cb1b7616108496cd3",
-        "internalRef": "350949863"
+        "sha": "8fa381b7138f1d72966ff20563efae1b2194d359",
+        "internalRef": "353145174"
       }
     },
     {

From 2f1450cb4780590d6660ebae56f96331b78dd973 Mon Sep 17 00:00:00 2001
From: Thiago Nunes 
Date: Mon, 25 Jan 2021 10:25:03 +1100
Subject: [PATCH 06/42] chore: warns of transactions bug (#815)

* chore: warns of transactions bug

Happening in versions >= 3.0.0 and <= 3.3.1

* chore: fix phrasing
---
 CHANGELOG.md | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6df923a9d96..9bb95f58712 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,15 @@
 
 ### [3.3.1](https://www.github.com/googleapis/java-spanner/compare/v3.3.0...v3.3.1) (2021-01-14)
 
+### ⚠ IMPORTANT: Known issue with this version of the client
+
+Since [v3.0.0](https://github.com/googleapis/java-spanner/releases/tag/v3.0.0), transactions can get stuck if the Spanner backend returns a retryable error when consuming the first record of a read / query in a transaction.
+
+A [fix](https://github.com/googleapis/java-spanner/pull/807) is submitted and available in version [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2)
+
+**Please use [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2) instead of this version.**
+
+Apologies for the inconvenience.
 
 ### Bug Fixes
 
@@ -31,6 +40,15 @@
 
 ## [3.3.0](https://www.github.com/googleapis/java-spanner/compare/v3.2.1...v3.3.0) (2021-01-07)
 
+### ⚠ IMPORTANT: Known issue with this version of the client
+
+Since [v3.0.0](https://github.com/googleapis/java-spanner/releases/tag/v3.0.0), transactions can get stuck if the Spanner backend returns a retryable error when consuming the first record of a read / query in a transaction.
+
+A [fix](https://github.com/googleapis/java-spanner/pull/807) is submitted and available in version [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2)
+
+**Please use [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2) instead of this version.**
+
+Apologies for the inconvenience.
 
 ### Features
 
@@ -43,6 +61,15 @@
 
 ### [3.2.1](https://www.github.com/googleapis/java-spanner/compare/v3.2.0...v3.2.1) (2021-01-06)
 
+### ⚠ IMPORTANT: Known issue with this version of the client
+
+Since [v3.0.0](https://github.com/googleapis/java-spanner/releases/tag/v3.0.0), transactions can get stuck if the Spanner backend returns a retryable error when consuming the first record of a read / query in a transaction.
+
+A [fix](https://github.com/googleapis/java-spanner/pull/807) is submitted and available in version [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2)
+
+**Please use [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2) instead of this version.**
+
+Apologies for the inconvenience.
 
 ### Bug Fixes
 
@@ -63,6 +90,15 @@
 
 ## [3.2.0](https://www.github.com/googleapis/java-spanner/compare/v3.1.3...v3.2.0) (2020-12-17)
 
+### ⚠ IMPORTANT: Known issue with this version of the client
+
+Since [v3.0.0](https://github.com/googleapis/java-spanner/releases/tag/v3.0.0), transactions can get stuck if the Spanner backend returns a retryable error when consuming the first record of a read / query in a transaction.
+
+A [fix](https://github.com/googleapis/java-spanner/pull/807) is submitted and available in version [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2)
+
+**Please use [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2) instead of this version.**
+
+Apologies for the inconvenience.
 
 ### Features
 
@@ -87,6 +123,15 @@
 
 ### [3.1.3](https://www.github.com/googleapis/java-spanner/compare/v3.1.2...v3.1.3) (2020-12-14)
 
+### ⚠ IMPORTANT: Known issue with this version of the client
+
+Since [v3.0.0](https://github.com/googleapis/java-spanner/releases/tag/v3.0.0), transactions can get stuck if the Spanner backend returns a retryable error when consuming the first record of a read / query in a transaction.
+
+A [fix](https://github.com/googleapis/java-spanner/pull/807) is submitted and available in version [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2)
+
+**Please use [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2) instead of this version.**
+
+Apologies for the inconvenience.
 
 ### Dependencies
 
@@ -94,6 +139,15 @@
 
 ### [3.1.2](https://www.github.com/googleapis/java-spanner/compare/v3.1.1...v3.1.2) (2020-12-14)
 
+### ⚠ IMPORTANT: Known issue with this version of the client
+
+Since [v3.0.0](https://github.com/googleapis/java-spanner/releases/tag/v3.0.0), transactions can get stuck if the Spanner backend returns a retryable error when consuming the first record of a read / query in a transaction.
+
+A [fix](https://github.com/googleapis/java-spanner/pull/807) is submitted and available in version [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2)
+
+**Please use [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2) instead of this version.**
+
+Apologies for the inconvenience.
 
 ### Dependencies
 
@@ -101,6 +155,15 @@
 
 ### [3.1.1](https://www.github.com/googleapis/java-spanner/compare/v3.1.0...v3.1.1) (2020-12-10)
 
+### ⚠ IMPORTANT: Known issue with this version of the client
+
+Since [v3.0.0](https://github.com/googleapis/java-spanner/releases/tag/v3.0.0), transactions can get stuck if the Spanner backend returns a retryable error when consuming the first record of a read / query in a transaction.
+
+A [fix](https://github.com/googleapis/java-spanner/pull/807) is submitted and available in version [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2)
+
+**Please use [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2) instead of this version.**
+
+Apologies for the inconvenience.
 
 ### Dependencies
 
@@ -108,6 +171,15 @@
 
 ## [3.1.0](https://www.github.com/googleapis/java-spanner/compare/v3.0.5...v3.1.0) (2020-12-10)
 
+### ⚠ IMPORTANT: Known issue with this version of the client
+
+Since [v3.0.0](https://github.com/googleapis/java-spanner/releases/tag/v3.0.0), transactions can get stuck if the Spanner backend returns a retryable error when consuming the first record of a read / query in a transaction.
+
+A [fix](https://github.com/googleapis/java-spanner/pull/807) is submitted and available in version [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2)
+
+**Please use [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2) instead of this version.**
+
+Apologies for the inconvenience.
 
 ### Features
 
@@ -130,6 +202,15 @@
 
 ### [3.0.5](https://www.github.com/googleapis/java-spanner/compare/v3.0.4...v3.0.5) (2020-11-19)
 
+### ⚠ IMPORTANT: Known issue with this version of the client
+
+Since [v3.0.0](https://github.com/googleapis/java-spanner/releases/tag/v3.0.0), transactions can get stuck if the Spanner backend returns a retryable error when consuming the first record of a read / query in a transaction.
+
+A [fix](https://github.com/googleapis/java-spanner/pull/807) is submitted and available in version [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2)
+
+**Please use [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2) instead of this version.**
+
+Apologies for the inconvenience.
 
 ### Bug Fixes
 
@@ -144,6 +225,15 @@
 
 ### [3.0.4](https://www.github.com/googleapis/java-spanner/compare/v3.0.3...v3.0.4) (2020-11-17)
 
+### ⚠ IMPORTANT: Known issue with this version of the client
+
+Since [v3.0.0](https://github.com/googleapis/java-spanner/releases/tag/v3.0.0), transactions can get stuck if the Spanner backend returns a retryable error when consuming the first record of a read / query in a transaction.
+
+A [fix](https://github.com/googleapis/java-spanner/pull/807) is submitted and available in version [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2)
+
+**Please use [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2) instead of this version.**
+
+Apologies for the inconvenience.
 
 ### Reverts
 
@@ -151,6 +241,15 @@
 
 ### [3.0.3](https://www.github.com/googleapis/java-spanner/compare/v3.0.2...v3.0.3) (2020-11-16)
 
+### ⚠ IMPORTANT: Known issue with this version of the client
+
+Since [v3.0.0](https://github.com/googleapis/java-spanner/releases/tag/v3.0.0), transactions can get stuck if the Spanner backend returns a retryable error when consuming the first record of a read / query in a transaction.
+
+A [fix](https://github.com/googleapis/java-spanner/pull/807) is submitted and available in version [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2)
+
+**Please use [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2) instead of this version.**
+
+Apologies for the inconvenience.
 
 ### Dependencies
 
@@ -158,6 +257,15 @@
 
 ### [3.0.2](https://www.github.com/googleapis/java-spanner/compare/v3.0.1...v3.0.2) (2020-11-13)
 
+### ⚠ IMPORTANT: Known issue with this version of the client
+
+Since [v3.0.0](https://github.com/googleapis/java-spanner/releases/tag/v3.0.0), transactions can get stuck if the Spanner backend returns a retryable error when consuming the first record of a read / query in a transaction.
+
+A [fix](https://github.com/googleapis/java-spanner/pull/807) is submitted and available in version [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2)
+
+**Please use [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2) instead of this version.**
+
+Apologies for the inconvenience.
 
 ### Bug Fixes
 
@@ -181,6 +289,15 @@
 
 ### [3.0.1](https://www.github.com/googleapis/java-spanner/compare/v3.0.0...v3.0.1) (2020-10-28)
 
+### ⚠ IMPORTANT: Known issue with this version of the client
+
+Since [v3.0.0](https://github.com/googleapis/java-spanner/releases/tag/v3.0.0), transactions can get stuck if the Spanner backend returns a retryable error when consuming the first record of a read / query in a transaction.
+
+A [fix](https://github.com/googleapis/java-spanner/pull/807) is submitted and available in version [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2)
+
+**Please use [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2) instead of this version.**
+
+Apologies for the inconvenience.
 
 ### Bug Fixes
 
@@ -196,6 +313,15 @@
 
 ## [3.0.0](https://www.github.com/googleapis/java-spanner/compare/v2.0.2...v3.0.0) (2020-10-23)
 
+### ⚠ IMPORTANT: Known issue with this version of the client
+
+Since [v3.0.0](https://github.com/googleapis/java-spanner/releases/tag/v3.0.0), transactions can get stuck if the Spanner backend returns a retryable error when consuming the first record of a read / query in a transaction.
+
+A [fix](https://github.com/googleapis/java-spanner/pull/807) is submitted and available in version [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2)
+
+**Please use [v3.3.2](https://github.com/googleapis/java-spanner/releases/tag/v3.3.2) instead of this version.**
+
+Apologies for the inconvenience.
 
 ### ⚠ BREAKING CHANGES
 

From f4a30d8add0964fe087f1b23037be53c4df2ba62 Mon Sep 17 00:00:00 2001
From: WhiteSource Renovate 
Date: Tue, 26 Jan 2021 19:30:22 +0100
Subject: [PATCH 07/42] build(deps): update dependency
 com.google.cloud:google-cloud-shared-config to v0.10.0 (#819)

[![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [com.google.cloud:google-cloud-shared-config](https://togithub.com/googleapis/java-shared-config) | `0.9.4` -> `0.10.0` | [![age](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/0.10.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/0.10.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/0.10.0/compatibility-slim/0.9.4)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/0.10.0/confidence-slim/0.9.4)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

googleapis/java-shared-config ### [`v0.10.0`](https://togithub.com/googleapis/java-shared-config/blob/master/CHANGELOG.md#​0100-httpswwwgithubcomgoogleapisjava-shared-configcomparev094v0100-2021-01-21) [Compare Source](https://togithub.com/googleapis/java-shared-config/compare/v0.9.4...v0.10.0) ##### Features - adding pom profile to generate docfx yml from javadoc ([#​213](https://www.github.com/googleapis/java-shared-config/issues/213)) ([3527c47](https://www.github.com/googleapis/java-shared-config/commit/3527c47ff413d415f87fccca84358da2c837841d)) ##### Dependencies - update dependency com.puppycrawl.tools:checkstyle to v8.39 ([#​209](https://www.github.com/googleapis/java-shared-config/issues/209)) ([fb53922](https://www.github.com/googleapis/java-shared-config/commit/fb539226d407001822a56c7fff792922cd85d1fe)) ##### [0.9.4](https://www.github.com/googleapis/java-shared-config/compare/v0.9.3...v0.9.4) (2020-10-21) ##### Documentation - Latest for Cloud-RAD ([#​199](https://www.github.com/googleapis/java-shared-config/issues/199)) ([34712af](https://www.github.com/googleapis/java-shared-config/commit/34712afac58aa0d148f0843026b3ff770ee030c2)) ##### [0.9.3](https://www.github.com/googleapis/java-shared-config/compare/v0.9.2...v0.9.3) (2020-10-15) ##### Dependencies - update auto-value-annotation.version to v1.7.4 ([#​157](https://www.github.com/googleapis/java-shared-config/issues/157)) ([5d7e394](https://www.github.com/googleapis/java-shared-config/commit/5d7e394d964010a3e32af492cec4be85aabc3ebf)) ##### [0.9.2](https://www.github.com/googleapis/java-shared-config/compare/v0.9.1...v0.9.2) (2020-07-02) ##### Dependencies - update dependency org.apache.maven.surefire:surefire-junit47 to v3.0.0-M5 ([#​180](https://www.github.com/googleapis/java-shared-config/issues/180)) ([802d9c5](https://www.github.com/googleapis/java-shared-config/commit/802d9c528d34b386face69ca75a014ce57fc3ac1)) ##### [0.9.1](https://www.github.com/googleapis/java-shared-config/compare/v0.9.0...v0.9.1) (2020-07-01) ##### Bug Fixes - maven-dependency-plugin configuration breaking downstream config ([#​174](https://www.github.com/googleapis/java-shared-config/issues/174)) ([507217f](https://www.github.com/googleapis/java-shared-config/commit/507217fe509cd4f16eb50c8075ab43229238e08d)) ##### Documentation - change Devsite output path to /java/docs/reference ([#​176](https://www.github.com/googleapis/java-shared-config/issues/176)) ([8b98af5](https://www.github.com/googleapis/java-shared-config/commit/8b98af54bf503d97bb86b6d02a5c4301b39384e1))
--- ### 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 41e4a0c513c..8abee2e6d37 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.4 + 0.10.0 Google Cloud Spanner BOM diff --git a/pom.xml b/pom.xml index 11254547acb..48a162429e7 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ com.google.cloud google-cloud-shared-config - 0.9.4 + 0.10.0 From 8024980326e465a8b4143a6251dc0d3f3a524f57 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Tue, 26 Jan 2021 11:16:39 -0800 Subject: [PATCH 08/42] chore: adding docfx doclet resource (#820) This PR was generated using Autosynth. :rainbow: Synth log will be available here: https://source.cloud.google.com/results/invocations/1376fa4d-7801-4cd6-9cc9-093147d097de/targets - [ ] To automatically regenerate this PR, check this box. Source-Link: https://github.com/googleapis/synthtool/commit/3816b080296d4d52975079fd26c110dd26ba25af --- .kokoro/release/publish_javadoc.cfg | 3 +++ synth.metadata | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.kokoro/release/publish_javadoc.cfg b/.kokoro/release/publish_javadoc.cfg index 2a5ec04f9b9..e41d593e948 100644 --- a/.kokoro/release/publish_javadoc.cfg +++ b/.kokoro/release/publish_javadoc.cfg @@ -27,3 +27,6 @@ before_action { } } } + +# Downloads docfx doclet resource. This will be in ${KOKORO_GFILE_DIR}/ +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/docfx" \ No newline at end of file diff --git a/synth.metadata b/synth.metadata index 554bb0a42ca..0767da9c0e4 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "4aa770ce669404ad0eca542ae5bd32a421748c18" + "sha": "2f1450cb4780590d6660ebae56f96331b78dd973" } }, { @@ -19,7 +19,7 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "6133907dbb3ddab204a17a15d5c53ec0aae9b033" + "sha": "3816b080296d4d52975079fd26c110dd26ba25af" } } ], From a9aa69a27575d00733d0d806b5acf23be32c59cd Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Thu, 28 Jan 2021 15:59:53 -0800 Subject: [PATCH 09/42] build: add 3.1.x branch as protected and releasable (#823) --- .github/release-please.yml | 6 +++++- .github/sync-repo-settings.yaml | 27 +++++++++++++++++++++++++++ synth.py | 2 ++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/.github/release-please.yml b/.github/release-please.yml index dce2c845092..cfb9fd5c507 100644 --- a/.github/release-please.yml +++ b/.github/release-please.yml @@ -1,2 +1,6 @@ releaseType: java-yoshi -bumpMinorPreMajor: true \ No newline at end of file +bumpMinorPreMajor: true +branches: +- branch: 3.1.x + releaseType: java-yoshi + bumpMinorPreMajor: true diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml index 6bddd18eac9..9eee4819794 100644 --- a/.github/sync-repo-settings.yaml +++ b/.github/sync-repo-settings.yaml @@ -39,6 +39,33 @@ branchProtectionRules: - "units (11)" - "Kokoro - Test: Integration" - "cla/google" + +# Identifies the protection rule pattern. Name of the branch to be protected. +# Defaults to `master` +- pattern: 3.1.x + # Can admins overwrite branch protection. + # Defaults to `true` + isAdminEnforced: true + # Number of approving reviews required to update matching branches. + # Defaults to `1` + requiredApprovingReviewCount: 1 + # Are reviews from code owners required to update matching branches. + # Defaults to `false` + requiresCodeOwnerReviews: true + # Require up to date branches + requiresStrictStatusChecks: false + # List of required status check contexts that must pass for commits to be accepted to matching branches. + requiredStatusCheckContexts: + - "dependencies (8)" + - "dependencies (11)" + - "linkage-monitor" + - "lint" + - "clirr" + - "units (7)" + - "units (8)" + - "units (11)" + - "Kokoro - Test: Integration" + - "cla/google" # List of explicit permissions to add (additive only) permissionRules: - team: yoshi-admins diff --git a/synth.py b/synth.py index 24d6b3074f4..a855da29c03 100644 --- a/synth.py +++ b/synth.py @@ -89,4 +89,6 @@ 'samples/snapshot/pom.xml', 'samples/snippets/pom.xml', '.github/CODEOWNERS', + '.github/sync-repo-settings.yaml', + '.github/release-please.yml', ]) From facda8a7d07a9cf08b1847678b24b270f1abdb38 Mon Sep 17 00:00:00 2001 From: Justin Beckwith Date: Thu, 28 Jan 2021 22:16:30 -0800 Subject: [PATCH 10/42] build: migrate to flakybot (#826) --- .kokoro/build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.kokoro/build.sh b/.kokoro/build.sh index 8280d10ad67..4572da72d57 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -110,8 +110,8 @@ bash .kokoro/coerce_logs.sh if [[ "${ENABLE_BUILD_COP}" == "true" ]] then - chmod +x ${KOKORO_GFILE_DIR}/linux_amd64/buildcop - ${KOKORO_GFILE_DIR}/linux_amd64/buildcop -repo=googleapis/java-spanner + chmod +x ${KOKORO_GFILE_DIR}/linux_amd64/flakybot + ${KOKORO_GFILE_DIR}/linux_amd64/flakybot -repo=googleapis/java-spanner fi echo "exiting with ${RETURN_CODE}" From e1e915289755e5f46ba07569d85afda5df5e3f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Sun, 31 Jan 2021 23:53:01 +0100 Subject: [PATCH 11/42] feat: allow session pool settings in connection url (#821) * feat: allow session pool settings in connection url * fix: use NoCredentials in test --- .../cloud/spanner/SessionPoolOptions.java | 61 ++++ .../spanner/connection/ConnectionOptions.java | 97 +++++- .../cloud/spanner/connection/SpannerPool.java | 8 +- .../connection/ConnectionOptionsTest.java | 24 ++ .../spanner/connection/ConnectionTest.java | 309 +++++++++++------- .../spanner/connection/SpannerPoolTest.java | 93 ++++-- 6 files changed, 432 insertions(+), 160 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java index 2c68fd317e8..c7432ea40db 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java @@ -18,6 +18,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; +import java.util.Objects; import org.threeten.bp.Duration; /** Options for the session pool used by {@code DatabaseClient}. */ @@ -63,6 +64,48 @@ private SessionPoolOptions(Builder builder) { this.removeInactiveSessionAfter = builder.removeInactiveSessionAfter; } + @Override + public boolean equals(Object o) { + if (!(o instanceof SessionPoolOptions)) { + return false; + } + SessionPoolOptions other = (SessionPoolOptions) o; + return Objects.equals(this.minSessions, other.minSessions) + && Objects.equals(this.maxSessions, other.maxSessions) + && Objects.equals(this.incStep, other.incStep) + && Objects.equals(this.maxIdleSessions, other.maxIdleSessions) + && Objects.equals(this.writeSessionsFraction, other.writeSessionsFraction) + && Objects.equals(this.actionOnExhaustion, other.actionOnExhaustion) + && Objects.equals(this.actionOnSessionNotFound, other.actionOnSessionNotFound) + && Objects.equals(this.actionOnSessionLeak, other.actionOnSessionLeak) + && Objects.equals( + this.initialWaitForSessionTimeoutMillis, other.initialWaitForSessionTimeoutMillis) + && Objects.equals(this.loopFrequency, other.loopFrequency) + && Objects.equals(this.keepAliveIntervalMinutes, other.keepAliveIntervalMinutes) + && Objects.equals(this.removeInactiveSessionAfter, other.removeInactiveSessionAfter); + } + + @Override + public int hashCode() { + return Objects.hash( + this.minSessions, + this.maxSessions, + this.incStep, + this.maxIdleSessions, + this.writeSessionsFraction, + this.actionOnExhaustion, + this.actionOnSessionNotFound, + this.actionOnSessionLeak, + this.initialWaitForSessionTimeoutMillis, + this.loopFrequency, + this.keepAliveIntervalMinutes, + this.removeInactiveSessionAfter); + } + + public Builder toBuilder() { + return new Builder(this); + } + public int getMinSessions() { return minSessions; } @@ -165,6 +208,24 @@ public static class Builder { private int keepAliveIntervalMinutes = 30; private Duration removeInactiveSessionAfter = Duration.ofMinutes(55L); + public Builder() {} + + private Builder(SessionPoolOptions options) { + this.minSessionsSet = true; + this.minSessions = options.minSessions; + this.maxSessions = options.maxSessions; + this.incStep = options.incStep; + this.maxIdleSessions = options.maxIdleSessions; + this.writeSessionsFraction = options.writeSessionsFraction; + this.actionOnExhaustion = options.actionOnExhaustion; + this.initialWaitForSessionTimeoutMillis = options.initialWaitForSessionTimeoutMillis; + this.actionOnSessionNotFound = options.actionOnSessionNotFound; + this.actionOnSessionLeak = options.actionOnSessionLeak; + this.loopFrequency = options.loopFrequency; + this.keepAliveIntervalMinutes = options.keepAliveIntervalMinutes; + this.removeInactiveSessionAfter = options.removeInactiveSessionAfter; + } + /** * Minimum number of sessions that this pool will always maintain. These will be created eagerly * in parallel. Defaults to 100. diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java index a668342f1bf..a688454f303 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java @@ -150,6 +150,8 @@ public String[] getValidValues() { static final boolean DEFAULT_RETRY_ABORTS_INTERNALLY = true; private static final String DEFAULT_CREDENTIALS = null; private static final String DEFAULT_OAUTH_TOKEN = null; + private static final String DEFAULT_MIN_SESSIONS = null; + private static final String DEFAULT_MAX_SESSIONS = null; private static final String DEFAULT_NUM_CHANNELS = null; private static final String DEFAULT_USER_AGENT = null; private static final String DEFAULT_OPTIMIZER_VERSION = ""; @@ -172,6 +174,10 @@ public String[] getValidValues() { * OAuth token to use for authentication. Cannot be used in combination with a credentials file. */ public static final String OAUTH_TOKEN_PROPERTY_NAME = "oauthToken"; + /** Name of the 'minSessions' connection property. */ + public static final String MIN_SESSIONS_PROPERTY_NAME = "minSessions"; + /** Name of the 'numChannels' connection property. */ + public static final String MAX_SESSIONS_PROPERTY_NAME = "maxSessions"; /** Name of the 'numChannels' connection property. */ public static final String NUM_CHANNELS_PROPERTY_NAME = "numChannels"; /** Custom user agent string is only for other Google libraries. */ @@ -204,6 +210,12 @@ public String[] getValidValues() { ConnectionProperty.createStringProperty( OAUTH_TOKEN_PROPERTY_NAME, "A valid pre-existing OAuth token to use for authentication for this connection. Setting this property will take precedence over any value set for a credentials file."), + ConnectionProperty.createStringProperty( + MIN_SESSIONS_PROPERTY_NAME, + "The minimum number of sessions in the backing session pool. The default is 100."), + ConnectionProperty.createStringProperty( + MAX_SESSIONS_PROPERTY_NAME, + "The maximum number of sessions in the backing session pool. The default is 400."), ConnectionProperty.createStringProperty( NUM_CHANNELS_PROPERTY_NAME, "The number of gRPC channels to use to communicate with Cloud Spanner. The default is 4."), @@ -327,6 +339,9 @@ private boolean isValidUri(String uri) { * true. *
  • readonly (boolean): Sets the initial readonly mode for the connection. Default is * false. + *
  • minSessions (int): Sets the minimum number of sessions in the backing session pool. + *
  • maxSessions (int): Sets the maximum number of sessions in the backing session pool. + *
  • numChannels (int): Sets the number of gRPC channels to use for the connection. *
  • retryAbortsInternally (boolean): Sets the initial retryAbortsInternally mode for the * connection. Default is true. *
  • optimizerVersion (string): Sets the query optimizer version to use for the connection. @@ -437,6 +452,8 @@ public static Builder newBuilder() { private final Credentials credentials; private final SessionPoolOptions sessionPoolOptions; private final Integer numChannels; + private final Integer minSessions; + private final Integer maxSessions; private final String userAgent; private final QueryOptions queryOptions; @@ -453,7 +470,6 @@ private ConnectionOptions(Builder builder) { this.warnings = checkValidProperties(builder.uri); this.uri = builder.uri; - this.sessionPoolOptions = builder.sessionPoolOptions; this.credentialsUrl = builder.credentialsUrl != null ? builder.credentialsUrl : parseCredentials(builder.uri); this.oauthToken = @@ -492,19 +508,12 @@ private ConnectionOptions(Builder builder) { } else { this.credentials = getCredentialsService().createCredentials(this.credentialsUrl); } - String numChannelsValue = parseNumChannels(builder.uri); - if (numChannelsValue != null) { - try { - this.numChannels = Integer.valueOf(numChannelsValue); - } catch (NumberFormatException e) { - throw SpannerExceptionFactory.newSpannerException( - ErrorCode.INVALID_ARGUMENT, - "Invalid numChannels value specified: " + numChannelsValue, - e); - } - } else { - this.numChannels = null; - } + this.minSessions = + parseIntegerProperty(MIN_SESSIONS_PROPERTY_NAME, parseMinSessions(builder.uri)); + this.maxSessions = + parseIntegerProperty(MAX_SESSIONS_PROPERTY_NAME, parseMaxSessions(builder.uri)); + this.numChannels = + parseIntegerProperty(NUM_CHANNELS_PROPERTY_NAME, parseNumChannels(builder.uri)); String projectId = matcher.group(Builder.PROJECT_GROUP); if (Builder.DEFAULT_PROJECT_ID_PLACEHOLDER.equalsIgnoreCase(projectId)) { @@ -518,6 +527,36 @@ private ConnectionOptions(Builder builder) { this.statementExecutionInterceptors = Collections.unmodifiableList(builder.statementExecutionInterceptors); this.configurator = builder.configurator; + + if (this.minSessions != null || this.maxSessions != null) { + SessionPoolOptions.Builder sessionPoolOptionsBuilder = + builder.sessionPoolOptions == null + ? SessionPoolOptions.newBuilder() + : builder.sessionPoolOptions.toBuilder(); + if (this.minSessions != null) { + sessionPoolOptionsBuilder.setMinSessions(this.minSessions); + } + if (this.maxSessions != null) { + sessionPoolOptionsBuilder.setMaxSessions(this.maxSessions); + } + this.sessionPoolOptions = sessionPoolOptionsBuilder.build(); + } else { + this.sessionPoolOptions = builder.sessionPoolOptions; + } + } + + private static Integer parseIntegerProperty(String propertyName, String value) { + if (value != null) { + try { + return Integer.valueOf(value); + } catch (NumberFormatException e) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.INVALID_ARGUMENT, + String.format("Invalid %s value specified: %s", propertyName, value), + e); + } + } + return null; } SpannerOptionsConfigurator getConfigurator() { @@ -565,6 +604,18 @@ static String parseOAuthToken(String uri) { return value != null ? value : DEFAULT_OAUTH_TOKEN; } + @VisibleForTesting + static String parseMinSessions(String uri) { + String value = parseUriProperty(uri, MIN_SESSIONS_PROPERTY_NAME); + return value != null ? value : DEFAULT_MIN_SESSIONS; + } + + @VisibleForTesting + static String parseMaxSessions(String uri) { + String value = parseUriProperty(uri, MAX_SESSIONS_PROPERTY_NAME); + return value != null ? value : DEFAULT_MAX_SESSIONS; + } + @VisibleForTesting static String parseNumChannels(String uri) { String value = parseUriProperty(uri, NUM_CHANNELS_PROPERTY_NAME); @@ -671,6 +722,24 @@ public SessionPoolOptions getSessionPoolOptions() { return sessionPoolOptions; } + /** + * The minimum number of sessions in the backing session pool of this connection. The session pool + * is shared between all connections in the same JVM that connect to the same Cloud Spanner + * database using the same connection settings. + */ + public Integer getMinSessions() { + return minSessions; + } + + /** + * The maximum number of sessions in the backing session pool of this connection. The session pool + * is shared between all connections in the same JVM that connect to the same Cloud Spanner + * database using the same connection settings. + */ + public Integer getMaxSessions() { + return maxSessions; + } + /** The number of channels to use for the connection. */ public Integer getNumChannels() { return numChannels; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java index de351c87c93..be5b16e393b 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java @@ -148,7 +148,8 @@ static class SpannerPoolKey { private final boolean usePlainText; private final String userAgent; - private static SpannerPoolKey of(ConnectionOptions options) { + @VisibleForTesting + static SpannerPoolKey of(ConnectionOptions options) { return new SpannerPoolKey(options); } @@ -156,7 +157,10 @@ private SpannerPoolKey(ConnectionOptions options) { this.host = options.getHost(); this.projectId = options.getProjectId(); this.credentialsKey = CredentialsKey.create(options); - this.sessionPoolOptions = options.getSessionPoolOptions(); + this.sessionPoolOptions = + options.getSessionPoolOptions() == null + ? SessionPoolOptions.newBuilder().build() + : options.getSessionPoolOptions(); this.numChannels = options.getNumChannels(); this.usePlainText = options.isUsePlainText(); this.userAgent = options.getUserAgent(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsTest.java index b2f4ea086ca..46452d3fc08 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsTest.java @@ -421,4 +421,28 @@ public void testLenient() { assertThat(e.getMessage()).contains("bar"); } } + + @Test + public void testMinSessions() { + ConnectionOptions options = + ConnectionOptions.newBuilder() + .setUri( + "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database?minSessions=400") + .setCredentialsUrl(FILE_TEST_PATH) + .build(); + assertThat(options.getMinSessions()).isEqualTo(400); + assertThat(options.getSessionPoolOptions().getMinSessions()).isEqualTo(400); + } + + @Test + public void testMaxSessions() { + ConnectionOptions options = + ConnectionOptions.newBuilder() + .setUri( + "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database?maxSessions=4000") + .setCredentialsUrl(FILE_TEST_PATH) + .build(); + assertThat(options.getMaxSessions()).isEqualTo(4000); + assertThat(options.getSessionPoolOptions().getMaxSessions()).isEqualTo(4000); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionTest.java index de820ccbcc5..4a26721fe8f 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionTest.java @@ -19,156 +19,241 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; import com.google.cloud.spanner.AbortedException; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; +import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; +import com.google.protobuf.AbstractMessage; +import com.google.spanner.v1.BatchCreateSessionsRequest; import com.google.spanner.v1.ExecuteSqlRequest; import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions; +import java.util.Arrays; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.AfterClass; import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; -public class ConnectionTest extends AbstractMockServerTest { - @Test - public void testDefaultOptimizerVersion() { - try (Connection connection = createConnection()) { - try (ResultSet rs = - connection.executeQuery(Statement.of("SHOW VARIABLE OPTIMIZER_VERSION"))) { - assertThat(rs.next()).isTrue(); - assertThat(rs.getString("OPTIMIZER_VERSION")).isEqualTo(""); - assertThat(rs.next()).isFalse(); - } - } - } +@RunWith(Enclosed.class) +public class ConnectionTest { - @Test - public void testUseOptimizerVersionFromEnvironment() { - try { - SpannerOptions.useEnvironment( - new SpannerOptions.SpannerEnvironment() { - @Override - public String getOptimizerVersion() { - return "20"; - } - }); + public static class DefaultConnectionOptionsTest extends AbstractMockServerTest { + @Test + public void testDefaultOptimizerVersion() { try (Connection connection = createConnection()) { - // Do a query and verify that the version from the environment is used. - try (ResultSet rs = connection.executeQuery(SELECT_COUNT_STATEMENT)) { - assertThat(rs.next()).isTrue(); - assertThat(rs.getLong(0)).isEqualTo(COUNT_BEFORE_INSERT); - assertThat(rs.next()).isFalse(); - // Verify query options from the environment. - ExecuteSqlRequest request = getLastExecuteSqlRequest(); - assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("20"); - } - // Now set one of the query options on the connection. That option should be used in - // combination with the other option from the environment. - connection.execute(Statement.of("SET OPTIMIZER_VERSION='30'")); - try (ResultSet rs = connection.executeQuery(SELECT_COUNT_STATEMENT)) { - assertThat(rs.next()).isTrue(); - assertThat(rs.getLong(0)).isEqualTo(COUNT_BEFORE_INSERT); - assertThat(rs.next()).isFalse(); - - ExecuteSqlRequest request = getLastExecuteSqlRequest(); - // Optimizer version should come from the connection. - assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("30"); - } - // Now specify options directly for the query. These should override both the environment - // and what is set on the connection. try (ResultSet rs = - connection.executeQuery( - Statement.newBuilder(SELECT_COUNT_STATEMENT.getSql()) - .withQueryOptions( - QueryOptions.newBuilder() - .setOptimizerVersion("user-defined-version") - .build()) - .build())) { + connection.executeQuery(Statement.of("SHOW VARIABLE OPTIMIZER_VERSION"))) { assertThat(rs.next()).isTrue(); - assertThat(rs.getLong(0)).isEqualTo(COUNT_BEFORE_INSERT); + assertThat(rs.getString("OPTIMIZER_VERSION")).isEqualTo(""); assertThat(rs.next()).isFalse(); - - ExecuteSqlRequest request = getLastExecuteSqlRequest(); - // Optimizer version should come from the query. - assertThat(request.getQueryOptions().getOptimizerVersion()) - .isEqualTo("user-defined-version"); } } - } finally { - SpannerOptions.useDefaultEnvironment(); } - } - @Test - public void testExecuteInvalidBatchUpdate() { - try (Connection connection = createConnection()) { + @Test + public void testUseOptimizerVersionFromEnvironment() { try { - connection.executeBatchUpdate(ImmutableList.of(INSERT_STATEMENT, SELECT_RANDOM_STATEMENT)); - fail("Missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + SpannerOptions.useEnvironment( + new SpannerOptions.SpannerEnvironment() { + @Override + public String getOptimizerVersion() { + return "20"; + } + }); + try (Connection connection = createConnection()) { + // Do a query and verify that the version from the environment is used. + try (ResultSet rs = connection.executeQuery(SELECT_COUNT_STATEMENT)) { + assertThat(rs.next()).isTrue(); + assertThat(rs.getLong(0)).isEqualTo(COUNT_BEFORE_INSERT); + assertThat(rs.next()).isFalse(); + // Verify query options from the environment. + ExecuteSqlRequest request = getLastExecuteSqlRequest(); + assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("20"); + } + // Now set one of the query options on the connection. That option should be used in + // combination with the other option from the environment. + connection.execute(Statement.of("SET OPTIMIZER_VERSION='30'")); + try (ResultSet rs = connection.executeQuery(SELECT_COUNT_STATEMENT)) { + assertThat(rs.next()).isTrue(); + assertThat(rs.getLong(0)).isEqualTo(COUNT_BEFORE_INSERT); + assertThat(rs.next()).isFalse(); + + ExecuteSqlRequest request = getLastExecuteSqlRequest(); + // Optimizer version should come from the connection. + assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("30"); + } + // Now specify options directly for the query. These should override both the environment + // and what is set on the connection. + try (ResultSet rs = + connection.executeQuery( + Statement.newBuilder(SELECT_COUNT_STATEMENT.getSql()) + .withQueryOptions( + QueryOptions.newBuilder() + .setOptimizerVersion("user-defined-version") + .build()) + .build())) { + assertThat(rs.next()).isTrue(); + assertThat(rs.getLong(0)).isEqualTo(COUNT_BEFORE_INSERT); + assertThat(rs.next()).isFalse(); + + ExecuteSqlRequest request = getLastExecuteSqlRequest(); + // Optimizer version should come from the query. + assertThat(request.getQueryOptions().getOptimizerVersion()) + .isEqualTo("user-defined-version"); + } + } + } finally { + SpannerOptions.useDefaultEnvironment(); } } - } - @Test - public void testQueryAborted() { - try (Connection connection = createConnection()) { - connection.setRetryAbortsInternally(false); - for (boolean abort : new Boolean[] {true, false}) { + @Test + public void testExecuteInvalidBatchUpdate() { + try (Connection connection = createConnection()) { try { - if (abort) { - mockSpanner.abortNextStatement(); + connection.executeBatchUpdate( + ImmutableList.of(INSERT_STATEMENT, SELECT_RANDOM_STATEMENT)); + fail("Missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + } + } + } + + @Test + public void testQueryAborted() { + try (Connection connection = createConnection()) { + connection.setRetryAbortsInternally(false); + for (boolean abort : new Boolean[] {true, false}) { + try { + if (abort) { + mockSpanner.abortNextStatement(); + } + connection.executeQuery(SELECT_RANDOM_STATEMENT); + assertThat(abort).isFalse(); + connection.commit(); + } catch (AbortedException e) { + assertThat(abort).isTrue(); + connection.rollback(); } - connection.executeQuery(SELECT_RANDOM_STATEMENT); - assertThat(abort).isFalse(); - connection.commit(); - } catch (AbortedException e) { - assertThat(abort).isTrue(); - connection.rollback(); } } } - } - @Test - public void testUpdateAborted() { - try (Connection connection = createConnection()) { - connection.setRetryAbortsInternally(false); - for (boolean abort : new Boolean[] {true, false}) { - try { - if (abort) { - mockSpanner.abortNextStatement(); + @Test + public void testUpdateAborted() { + try (Connection connection = createConnection()) { + connection.setRetryAbortsInternally(false); + for (boolean abort : new Boolean[] {true, false}) { + try { + if (abort) { + mockSpanner.abortNextStatement(); + } + connection.executeUpdate(INSERT_STATEMENT); + assertThat(abort).isFalse(); + connection.commit(); + } catch (AbortedException e) { + assertThat(abort).isTrue(); + connection.rollback(); } - connection.executeUpdate(INSERT_STATEMENT); - assertThat(abort).isFalse(); - connection.commit(); - } catch (AbortedException e) { - assertThat(abort).isTrue(); - connection.rollback(); } } } - } - @Test - public void testBatchUpdateAborted() { - try (Connection connection = createConnection()) { - connection.setRetryAbortsInternally(false); - for (boolean abort : new Boolean[] {true, false}) { - try { - if (abort) { - mockSpanner.abortNextStatement(); + @Test + public void testBatchUpdateAborted() { + try (Connection connection = createConnection()) { + connection.setRetryAbortsInternally(false); + for (boolean abort : new Boolean[] {true, false}) { + try { + if (abort) { + mockSpanner.abortNextStatement(); + } + connection.executeBatchUpdate(ImmutableList.of(INSERT_STATEMENT, INSERT_STATEMENT)); + assertThat(abort).isFalse(); + connection.commit(); + } catch (AbortedException e) { + assertThat(abort).isTrue(); + connection.rollback(); } - connection.executeBatchUpdate(ImmutableList.of(INSERT_STATEMENT, INSERT_STATEMENT)); - assertThat(abort).isFalse(); - connection.commit(); - } catch (AbortedException e) { - assertThat(abort).isTrue(); - connection.rollback(); } } } } + + public static class ConnectionMinSessionsTest extends AbstractMockServerTest { + + @AfterClass + public static void reset() { + mockSpanner.reset(); + } + + protected String getBaseUrl() { + return super.getBaseUrl() + ";minSessions=1"; + } + + @Test + public void testMinSessions() throws InterruptedException, TimeoutException { + try (Connection connection = createConnection()) { + mockSpanner.waitForRequestsToContain( + new Predicate() { + @Override + public boolean apply(AbstractMessage input) { + return input instanceof BatchCreateSessionsRequest + && ((BatchCreateSessionsRequest) input).getSessionCount() == 1; + } + }, + 5000L); + } + } + } + + public static class ConnectionMaxSessionsTest extends AbstractMockServerTest { + + @AfterClass + public static void reset() { + mockSpanner.reset(); + } + + protected String getBaseUrl() { + return super.getBaseUrl() + ";maxSessions=1"; + } + + @Test + public void testMaxSessions() + throws InterruptedException, TimeoutException, ExecutionException { + try (Connection connection1 = createConnection(); + Connection connection2 = createConnection()) { + connection1.beginTransactionAsync(); + connection2.beginTransactionAsync(); + + ApiFuture count1 = connection1.executeUpdateAsync(INSERT_STATEMENT); + ApiFuture count2 = connection2.executeUpdateAsync(INSERT_STATEMENT); + + // Commit the transactions. Both should be able to finish, but both used the same session. + ApiFuture commit1 = connection1.commitAsync(); + ApiFuture commit2 = connection2.commitAsync(); + + // At least one transaction must wait until the other has finished before it can get a + // session. + assertThat(count1.isDone() && count2.isDone()).isFalse(); + assertThat(commit1.isDone() && commit2.isDone()).isFalse(); + + // Wait until both finishes. + ApiFutures.allAsList(Arrays.asList(commit1, commit2)).get(5L, TimeUnit.SECONDS); + + assertThat(count1.isDone()).isTrue(); + assertThat(count2.isDone()).isTrue(); + } + assertThat(mockSpanner.numSessionsCreated()).isEqualTo(1); + } + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java index afc0512b4ec..19d6dfddcde 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java @@ -16,10 +16,7 @@ package com.google.cloud.spanner.connection; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.MatcherAssert.assertThat; +import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -33,6 +30,7 @@ import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.connection.ConnectionImpl.LeakedConnectionException; import com.google.cloud.spanner.connection.SpannerPool.CheckAndCloseSpannersMode; +import com.google.cloud.spanner.connection.SpannerPool.SpannerPoolKey; import com.google.common.base.Ticker; import com.google.common.testing.FakeTicker; import java.io.ByteArrayOutputStream; @@ -108,40 +106,40 @@ public void testGetSpanner() { // assert equal spanner1 = pool.getSpanner(options1, connection1); spanner2 = pool.getSpanner(options1, connection2); - assertThat(spanner1, is(equalTo(spanner2))); + assertThat(spanner1).isEqualTo(spanner2); spanner1 = pool.getSpanner(options2, connection1); spanner2 = pool.getSpanner(options2, connection2); - assertThat(spanner1, is(equalTo(spanner2))); + assertThat(spanner1).isEqualTo(spanner2); spanner1 = pool.getSpanner(options3, connection1); spanner2 = pool.getSpanner(options3, connection2); - assertThat(spanner1, is(equalTo(spanner2))); + assertThat(spanner1).isEqualTo(spanner2); spanner1 = pool.getSpanner(options4, connection1); spanner2 = pool.getSpanner(options4, connection2); - assertThat(spanner1, is(equalTo(spanner2))); + assertThat(spanner1).isEqualTo(spanner2); // Options 5 and 6 both use default credentials. spanner1 = pool.getSpanner(options5, connection1); spanner2 = pool.getSpanner(options6, connection2); - assertThat(spanner1, is(equalTo(spanner2))); + assertThat(spanner1).isEqualTo(spanner2); // assert not equal spanner1 = pool.getSpanner(options1, connection1); spanner2 = pool.getSpanner(options2, connection2); - assertThat(spanner1, not(equalTo(spanner2))); + assertThat(spanner1).isNotEqualTo(spanner2); spanner1 = pool.getSpanner(options1, connection1); spanner2 = pool.getSpanner(options3, connection2); - assertThat(spanner1, not(equalTo(spanner2))); + assertThat(spanner1).isNotEqualTo(spanner2); spanner1 = pool.getSpanner(options1, connection1); spanner2 = pool.getSpanner(options4, connection2); - assertThat(spanner1, not(equalTo(spanner2))); + assertThat(spanner1).isNotEqualTo(spanner2); spanner1 = pool.getSpanner(options2, connection1); spanner2 = pool.getSpanner(options3, connection2); - assertThat(spanner1, not(equalTo(spanner2))); + assertThat(spanner1).isNotEqualTo(spanner2); spanner1 = pool.getSpanner(options2, connection1); spanner2 = pool.getSpanner(options4, connection2); - assertThat(spanner1, not(equalTo(spanner2))); + assertThat(spanner1).isNotEqualTo(spanner2); spanner1 = pool.getSpanner(options3, connection1); spanner2 = pool.getSpanner(options4, connection2); - assertThat(spanner1, not(equalTo(spanner2))); + assertThat(spanner1).isNotEqualTo(spanner2); } @Test @@ -153,17 +151,17 @@ public void testRemoveConnection() { // assert equal spanner1 = pool.getSpanner(options1, connection1); spanner2 = pool.getSpanner(options1, connection2); - assertThat(spanner1, is(equalTo(spanner2))); + assertThat(spanner1).isEqualTo(spanner2); // one connection removed, assert that we would still get the same Spanner pool.removeConnection(options1, connection1); spanner1 = pool.getSpanner(options1, connection1); - assertThat(spanner1, is(equalTo(spanner2))); + assertThat(spanner1).isEqualTo(spanner2); // remove two connections, assert that we would still get the same Spanner, as Spanners are not // directly closed and removed. pool.removeConnection(options1, connection1); pool.removeConnection(options1, connection2); spanner1 = pool.getSpanner(options1, connection1); - assertThat(spanner1, is(equalTo(spanner2))); + assertThat(spanner1).isEqualTo(spanner2); // remove the last connection again pool.removeConnection(options1, connection1); } @@ -200,7 +198,7 @@ public void testRemoveConnectionOptionsNotRegistered() { pool.getSpanner(options1, connection1); pool.removeConnection(options2, connection1); String capturedLog = getTestCapturedLog(); - assertThat(capturedLog.contains(expectedLogPart), is(true)); + assertThat(capturedLog.contains(expectedLogPart)).isTrue(); } @Test @@ -211,7 +209,7 @@ public void testRemoveConnectionConnectionNotRegistered() { pool.getSpanner(options1, connection1); pool.removeConnection(options1, connection2); String capturedLog = getTestCapturedLog(); - assertThat(capturedLog.contains(expectedLogPart), is(true)); + assertThat(capturedLog.contains(expectedLogPart)).isTrue(); } @Test @@ -223,7 +221,7 @@ public void testRemoveConnectionConnectionAlreadyRemoved() { pool.removeConnection(options1, connection1); pool.removeConnection(options1, connection1); String capturedLog = getTestCapturedLog(); - assertThat(capturedLog.contains(expectedLogPart), is(true)); + assertThat(capturedLog.contains(expectedLogPart)).isTrue(); } @Test @@ -237,7 +235,7 @@ public void testCloseSpanner() { } catch (SpannerException e) { exception = e.getErrorCode() == ErrorCode.FAILED_PRECONDITION; } - assertThat(exception, is(true)); + assertThat(exception).isTrue(); // remove the connection and verify that it is possible to close pool.removeConnection(options1, connection1); @@ -249,7 +247,7 @@ public void testCloseSpanner() { Spanner spanner2 = pool.getSpanner(options1, connection1); pool.checkAndCloseSpanners(CheckAndCloseSpannersMode.WARN); String capturedLog = getTestCapturedLog(); - assertThat(capturedLog.contains(expectedLogPart), is(true)); + assertThat(capturedLog.contains(expectedLogPart)).isTrue(); verify(spanner2, never()).close(); // remove the connection and verify that it is possible to close @@ -273,11 +271,11 @@ public void testLeakedConnection() { ConnectionOptions.closeSpanner(); fail("missing expected exception"); } catch (SpannerException e) { - assertThat(e.getErrorCode(), is(equalTo(ErrorCode.FAILED_PRECONDITION))); + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); } String capturedLog = getTestCapturedLog(); - assertThat(capturedLog.contains(LeakedConnectionException.class.getName()), is(true)); - assertThat(capturedLog.contains("testLeakedConnection"), is(true)); + assertThat(capturedLog.contains(LeakedConnectionException.class.getName())).isTrue(); + assertThat(capturedLog.contains("testLeakedConnection")).isTrue(); // Now close the connection to avoid trouble with other test cases. connection.close(); } @@ -292,7 +290,7 @@ public void testCloseUnusedSpanners() { // create two connections that use the same Spanner spanner1 = pool.getSpanner(options1, connection1); spanner2 = pool.getSpanner(options1, connection2); - assertThat(spanner1, is(equalTo(spanner2))); + assertThat(spanner1).isEqualTo(spanner2); // all spanners are in use, this should have no effect pool.closeUnusedSpanners(-1L); @@ -312,8 +310,8 @@ public void testCloseUnusedSpanners() { spanner1 = pool.getSpanner(options1, connection1); spanner2 = pool.getSpanner(options2, connection2); spanner3 = pool.getSpanner(options2, connection3); - assertThat(spanner1, not(equalTo(spanner2))); - assertThat(spanner2, is(equalTo(spanner3))); + assertThat(spanner1).isNotEqualTo(spanner2); + assertThat(spanner2).isEqualTo(spanner3); // all spanners are in use, this should have no effect pool.closeUnusedSpanners(-1L); @@ -359,7 +357,7 @@ public void testAutomaticCloser() throws InterruptedException { // create two connections that use the same Spanner spanner1 = pool.getSpanner(options1, connection1); spanner2 = pool.getSpanner(options1, connection2); - assertThat(spanner1, is(equalTo(spanner2))); + assertThat(spanner1).isEqualTo(spanner2); // all spanners are in use, this should have no effect ticker.advance(TEST_AUTOMATIC_CLOSE_TIMEOUT_NANOS + MILLISECOND); @@ -382,8 +380,8 @@ public void testAutomaticCloser() throws InterruptedException { spanner1 = pool.getSpanner(options1, connection1); spanner2 = pool.getSpanner(options2, connection2); spanner3 = pool.getSpanner(options2, connection3); - assertThat(spanner1, not(equalTo(spanner2))); - assertThat(spanner2, is(equalTo(spanner3))); + assertThat(spanner1).isNotEqualTo(spanner2); + assertThat(spanner2).isEqualTo(spanner3); // all spanners are in use, this should have no effect ticker.advance(TEST_AUTOMATIC_CLOSE_TIMEOUT_NANOS + MILLISECOND); @@ -416,4 +414,35 @@ public void testAutomaticCloser() throws InterruptedException { verify(spanner2).close(); verify(spanner3).close(); } + + @Test + public void testSpannerPoolKeyEquality() { + ConnectionOptions options1 = + ConnectionOptions.newBuilder() + .setUri( + "cloudspanner:/projects/p/instances/i/databases/d?minSessions=200;maxSessions=400") + .setCredentials(NoCredentials.getInstance()) + .build(); + // options2 equals the default session pool options, and is therefore equal to ConnectionOptions + // without any session pool configuration. + ConnectionOptions options2 = + ConnectionOptions.newBuilder() + .setUri( + "cloudspanner:/projects/p/instances/i/databases/d?minSessions=100;maxSessions=400") + .setCredentials(NoCredentials.getInstance()) + .build(); + ConnectionOptions options3 = + ConnectionOptions.newBuilder() + .setUri("cloudspanner:/projects/p/instances/i/databases/d") + .setCredentials(NoCredentials.getInstance()) + .build(); + + SpannerPoolKey key1 = SpannerPoolKey.of(options1); + SpannerPoolKey key2 = SpannerPoolKey.of(options2); + SpannerPoolKey key3 = SpannerPoolKey.of(options3); + + assertThat(key1).isNotEqualTo(key2); + assertThat(key2).isEqualTo(key3); + assertThat(key1).isNotEqualTo(key3); + } } From eaf673dd79cbb3fc99807764eb698d6344da3c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Sun, 31 Jan 2021 23:54:16 +0100 Subject: [PATCH 12/42] test: remove write fraction from test (#832) --- .../cloud/spanner/SessionPoolStressTest.java | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolStressTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolStressTest.java index a3b2a3c5425..2f0cd239abd 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolStressTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolStressTest.java @@ -62,9 +62,6 @@ public class SessionPoolStressTest extends BaseSessionPoolTest { @Parameter(0) - public double writeSessionsFraction; - - @Parameter(1) public boolean shouldBlock; DatabaseId db = DatabaseId.of("projects/p/instances/i/databases/unused"); @@ -85,13 +82,11 @@ public class SessionPoolStressTest extends BaseSessionPoolTest { int minSessionsWhenSessionClosed = Integer.MAX_VALUE; Exception e; - @Parameters(name = "write fraction = {0}, should block = {1}") + @Parameters(name = "should block = {0}") public static Collection data() { List params = new ArrayList<>(); - for (double writeFraction = 0; writeFraction <= 1; writeFraction += 0.5) { - params.add(new Object[] {writeFraction, true}); - params.add(new Object[] {writeFraction, false}); - } + params.add(new Object[] {true}); + params.add(new Object[] {false}); return params; } @@ -236,12 +231,8 @@ public void stressTest() throws Exception { setupSpanner(db); int minSessions = 2; int maxSessions = concurrentThreads / 2; - float writeSessionsFraction = 0.5f; SessionPoolOptions.Builder builder = - SessionPoolOptions.newBuilder() - .setMinSessions(minSessions) - .setMaxSessions(maxSessions) - .setWriteSessionsFraction(writeSessionsFraction); + SessionPoolOptions.newBuilder().setMinSessions(minSessions).setMaxSessions(maxSessions); if (shouldBlock) { builder.setBlockIfPoolExhausted(); } else { @@ -273,7 +264,7 @@ public void run() { PooledSessionFuture session = pool.getSession(); session.get(); Uninterruptibles.sleepUninterruptibly( - random.nextInt(5), TimeUnit.MILLISECONDS); + random.nextInt(2), TimeUnit.MILLISECONDS); resetTransaction(session.get().delegate); session.close(); } catch (SpannerException e) { From 816043b366740c62280e70b72bc996de84118271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Sun, 31 Jan 2021 23:54:58 +0100 Subject: [PATCH 13/42] test: reduce number of stress test runs (#833) --- .../com/google/cloud/spanner/AsyncResultSetImplStressTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncResultSetImplStressTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncResultSetImplStressTest.java index 71447d46e7e..0c1fb017046 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncResultSetImplStressTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncResultSetImplStressTest.java @@ -55,7 +55,7 @@ @RunWith(Parameterized.class) public class AsyncResultSetImplStressTest { - private static final int TEST_RUNS = 1000; + private static final int TEST_RUNS = 25; /** Timeout is applied to each test case individually. */ @Rule public Timeout timeout = new Timeout(120, TimeUnit.SECONDS); From 41f4aad5163d598b61a714ab72089b9359997542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Mon, 1 Feb 2021 00:03:57 +0100 Subject: [PATCH 14/42] test: skip timeout tests for non-idempotent methods (#836) Skips tests for DEADLINE_EXCEEDED errors for non-idempotent methods as they won't retry anyways. Speeds up test execution with approx 2 seconds. --- .../cloud/spanner/InstanceAdminGaxTest.java | 49 ++++++++++++------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceAdminGaxTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceAdminGaxTest.java index 5f3063bc7b2..34754e72719 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceAdminGaxTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceAdminGaxTest.java @@ -96,7 +96,7 @@ public synchronized Throwable getCause() { private static Exception createDelayedInternal() { return new DelayedStatusRuntimeException( io.grpc.Status.INTERNAL.withDescription("Delayed test exception.").asRuntimeException(), - 500L); + 1000L); } public static enum ExceptionType { @@ -224,12 +224,13 @@ public void setUp() throws Exception { RetrySettings retrySettingsWithLowTimeout = RetrySettings.newBuilder() .setInitialRetryDelay(Duration.ofMillis(1L)) - .setMaxRetryDelay(Duration.ofMillis(1000L)) + .setMaxRetryDelay(Duration.ofMillis(10L)) .setInitialRpcTimeout(Duration.ofMillis(20L)) .setMaxRpcTimeout(Duration.ofMillis(200L)) - .setRetryDelayMultiplier(1000.0d) + .setRetryDelayMultiplier(1.3d) .setMaxAttempts(10) .setTotalTimeout(Duration.ofMillis(20000L)) + .setJittered(false) .build(); RetrySettings retrySettingsWithHighTimeout = RetrySettings.newBuilder() @@ -453,6 +454,19 @@ public void getInstanceTest() { @Test public void createInstanceTest() throws Exception { + boolean methodIsIdempotent = + !spanner + .getOptions() + .getInstanceAdminStubSettings() + .createInstanceOperationSettings() + .getInitialCallSettings() + .getRetryableCodes() + .isEmpty(); + if (!methodIsIdempotent && exceptionType == ExceptionType.DELAYED) { + // Skip this test as the method is non-idempotent and won't retry anyways. + return; + } + Exception exception = setupException(); InstanceName name = InstanceName.of(PROJECT, "INSTANCE"); InstanceConfigName config = InstanceConfigName.of(PROJECT, "INSTANCE_CONFIG"); @@ -480,14 +494,6 @@ public void createInstanceTest() throws Exception { } mockInstanceAdmin.addResponse(resultOperation); - boolean methodIsIdempotent = - !spanner - .getOptions() - .getInstanceAdminStubSettings() - .createInstanceOperationSettings() - .getInitialCallSettings() - .getRetryableCodes() - .isEmpty(); for (int i = 0; i < 2; i++) { OperationFuture actualResponse = client.createInstance( @@ -518,6 +524,19 @@ public void createInstanceTest() throws Exception { @Test public void updateInstanceTest() throws Exception { + boolean methodIsIdempotent = + !spanner + .getOptions() + .getInstanceAdminStubSettings() + .updateInstanceOperationSettings() + .getInitialCallSettings() + .getRetryableCodes() + .isEmpty(); + if (!methodIsIdempotent && exceptionType == ExceptionType.DELAYED) { + // Skip this test as the method is non-idempotent and won't retry anyways. + return; + } + Exception exception = setupException(); InstanceName name = InstanceName.of(PROJECT, "INSTANCE"); InstanceConfigName config = InstanceConfigName.of(PROJECT, "INSTANCE_CONFIG"); @@ -545,14 +564,6 @@ public void updateInstanceTest() throws Exception { } mockInstanceAdmin.addResponse(resultOperation); - boolean methodIsIdempotent = - !spanner - .getOptions() - .getInstanceAdminStubSettings() - .updateInstanceOperationSettings() - .getInitialCallSettings() - .getRetryableCodes() - .isEmpty(); for (int i = 0; i < 2; i++) { OperationFuture actualResponse = client.updateInstance( From f218a19f273fa3fd6d2cd5da5731e5a818adeb22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Mon, 1 Feb 2021 00:24:11 +0100 Subject: [PATCH 15/42] test: unflake testCloseResultSetWhileRequestInFlight (#834) --- .../cloud/spanner/AbstractResultSet.java | 8 +- .../spanner/InlineBeginTransactionTest.java | 2869 +++++++++-------- .../cloud/spanner/MockSpannerServiceImpl.java | 6 +- 3 files changed, 1451 insertions(+), 1432 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java index 1d26adc757b..93f7e6cb0ce 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java @@ -786,7 +786,7 @@ static class GrpcStreamIterator extends AbstractIterator private final Statement statement; private SpannerRpc.StreamingCall call; - private boolean withBeginTransaction; + private volatile boolean withBeginTransaction; private SpannerException error; @VisibleForTesting @@ -889,12 +889,6 @@ public void onError(SpannerException e) { error = e; addToStream(END_OF_STREAM); } - - // Visible only for testing. - @VisibleForTesting - void setCall(SpannerRpc.StreamingCall call, boolean withBeginTransaction) { - GrpcStreamIterator.this.setCall(call, withBeginTransaction); - } } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java index e86ffd85543..aeeac5b3d11 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java @@ -61,6 +61,7 @@ import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; @@ -68,6 +69,7 @@ import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.After; @@ -75,25 +77,15 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; -@RunWith(Parameterized.class) +@RunWith(Enclosed.class) public class InlineBeginTransactionTest { - @Parameter public Executor executor; - - @Parameters(name = "executor = {0}") - public static Collection data() { - return Arrays.asList( - new Object[][] { - {MoreExecutors.directExecutor()}, - {Executors.newSingleThreadExecutor()}, - {Executors.newFixedThreadPool(4)} - }); - } - private static MockSpannerServiceImpl mockSpanner; private static Server server; private static LocalChannelProvider channelProvider; @@ -144,7 +136,7 @@ public static Collection data() { private static final Statement READ_ROW_STATEMENT = Statement.of("SELECT BAR FROM FOO WHERE ID=1"); - private Spanner spanner; + protected Spanner spanner; @BeforeClass public static void startStaticServer() throws IOException { @@ -209,1349 +201,1229 @@ public void tearDown() throws Exception { mockSpanner.clearRequests(); } - @Test - public void testInlinedBeginTx() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - long updateCount = - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(TransactionContext transaction) throws Exception { - return transaction.executeUpdate(UPDATE_STATEMENT); - } - }); - assertThat(updateCount).isEqualTo(UPDATE_COUNT); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - assertThat(countTransactionsStarted()).isEqualTo(1); - } + @RunWith(Parameterized.class) + public static class InlineBeginTransactionWithExecutorTest extends InlineBeginTransactionTest { + @Parameter public Executor executor; + + @Parameters(name = "executor = {0}") + public static Collection data() { + return Arrays.asList( + new Object[][] { + {MoreExecutors.directExecutor()}, + {Executors.newSingleThreadExecutor()}, + {Executors.newFixedThreadPool(4)} + }); + } - @Test - public void testInlinedBeginTxAborted() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - final AtomicBoolean firstAttempt = new AtomicBoolean(true); - long updateCount = - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(TransactionContext transaction) throws Exception { - long res = transaction.executeUpdate(UPDATE_STATEMENT); - if (firstAttempt.getAndSet(false)) { - mockSpanner.abortTransaction(transaction); + @Test + public void testInlinedBeginAsyncTx() throws InterruptedException, ExecutionException { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + ApiFuture updateCount = + client + .runAsync() + .runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + return txn.executeUpdateAsync(UPDATE_STATEMENT); } - return res; - } - }); - assertThat(updateCount).isEqualTo(UPDATE_COUNT); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(2); - // We have started 2 transactions, because the first transaction aborted during the commit. - assertThat(countRequests(CommitRequest.class)).isEqualTo(2); - assertThat(countTransactionsStarted()).isEqualTo(2); - } + }, + executor); + assertThat(updateCount.get()).isEqualTo(UPDATE_COUNT); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countTransactionsStarted()).isEqualTo(1); + } - @Test - public void testInlinedBeginFirstUpdateAborts() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - long updateCount = - client - .readWriteTransaction() - .run( - new TransactionCallable() { - boolean firstAttempt = true; + @Test + public void testInlinedBeginAsyncTxAborted() throws InterruptedException, ExecutionException { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + final AtomicBoolean firstAttempt = new AtomicBoolean(true); + ApiFuture updateCount = + client + .runAsync() + .runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + ApiFuture res = txn.executeUpdateAsync(UPDATE_STATEMENT); + if (firstAttempt.getAndSet(false)) { + mockSpanner.abortTransaction(txn); + } + return res; + } + }, + executor); + assertThat(updateCount.get()).isEqualTo(UPDATE_COUNT); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + // We have started 2 transactions, because the first transaction aborted. + assertThat(countTransactionsStarted()).isEqualTo(2); + } - @Override - public Long run(TransactionContext transaction) throws Exception { - if (firstAttempt) { - firstAttempt = false; - mockSpanner.putStatementResult( - StatementResult.exception( - UPDATE_STATEMENT, - mockSpanner.createAbortedException( - ByteString.copyFromUtf8("some-tx")))); - } else { - mockSpanner.putStatementResult( - StatementResult.update(UPDATE_STATEMENT, UPDATE_COUNT)); + @Test + public void testInlinedBeginAsyncTxWithQuery() throws InterruptedException, ExecutionException { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + final ExecutorService queryExecutor = Executors.newSingleThreadExecutor(); + ApiFuture updateCount = + client + .runAsync() + .runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + final SettableApiFuture res = SettableApiFuture.create(); + try (AsyncResultSet rs = txn.executeQueryAsync(SELECT1)) { + rs.setCallback( + executor, + new ReadyCallback() { + @Override + public CallbackResponse cursorReady(AsyncResultSet resultSet) { + switch (resultSet.tryNext()) { + case DONE: + return CallbackResponse.DONE; + case NOT_READY: + return CallbackResponse.CONTINUE; + case OK: + res.set(resultSet.getLong(0)); + default: + throw new IllegalStateException(); + } + } + }); + } + return res; } - return transaction.executeUpdate(UPDATE_STATEMENT); - } - }); - assertThat(updateCount).isEqualTo(UPDATE_COUNT); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(2); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - } + }, + queryExecutor); + assertThat(updateCount.get()).isEqualTo(1L); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countTransactionsStarted()).isEqualTo(1); + queryExecutor.shutdown(); + } - @Test - public void testInlinedBeginFirstQueryAborts() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - long updateCount = - client - .readWriteTransaction() - .run( - new TransactionCallable() { - boolean firstAttempt = true; + @Test + public void testInlinedBeginAsyncTxWithBatchDml() + throws InterruptedException, ExecutionException { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + ApiFuture updateCounts = + client + .runAsync() + .runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext transaction) { + return transaction.batchUpdateAsync( + Arrays.asList(UPDATE_STATEMENT, UPDATE_STATEMENT)); + } + }, + executor); + assertThat(updateCounts.get()).asList().containsExactly(UPDATE_COUNT, UPDATE_COUNT); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countTransactionsStarted()).isEqualTo(1); + } - @Override - public Long run(TransactionContext transaction) throws Exception { - if (firstAttempt) { - firstAttempt = false; - mockSpanner.putStatementResult( - StatementResult.exception( - SELECT1, - mockSpanner.createAbortedException( - ByteString.copyFromUtf8("some-tx")))); - } else { - mockSpanner.putStatementResult( - StatementResult.query(SELECT1, SELECT1_RESULTSET)); + @Test + public void testInlinedBeginAsyncTxWithError() throws InterruptedException, ExecutionException { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + ApiFuture updateCount = + client + .runAsync() + .runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext transaction) { + transaction.executeUpdateAsync(INVALID_UPDATE_STATEMENT); + return transaction.executeUpdateAsync(UPDATE_STATEMENT); + } + }, + executor); + assertThat(updateCount.get()).isEqualTo(UPDATE_COUNT); + // The first statement will fail and not return a transaction id. This will trigger a retry of + // the entire transaction, and the retry will do an explicit BeginTransaction RPC. + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); + // The first update will start a transaction, but then fail the update statement. This will + // start a transaction on the mock server, but that transaction will never be returned to the + // client. + assertThat(countTransactionsStarted()).isEqualTo(2); + } + + @Test + public void testInlinedBeginAsyncTxWithOnlyMutations() + throws InterruptedException, ExecutionException { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + client + .runAsync() + .runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext transaction) { + transaction.buffer(Mutation.newInsertBuilder("FOO").set("ID").to(1L).build()); + return ApiFutures.immediateFuture(null); + } + }, + executor) + .get(); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + assertThat(countTransactionsStarted()).isEqualTo(1); + } + + @Test + public void testAsyncTransactionManagerInlinedBeginTx() + throws InterruptedException, ExecutionException { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + try (AsyncTransactionManager txMgr = client.transactionManagerAsync()) { + TransactionContextFuture txn = txMgr.beginAsync(); + while (true) { + AsyncTransactionStep updateCount = + txn.then( + new AsyncTransactionFunction() { + @Override + public ApiFuture apply(TransactionContext txn, Void input) + throws Exception { + return txn.executeUpdateAsync(UPDATE_STATEMENT); } - try (ResultSet rs = transaction.executeQuery(SELECT1)) { - while (rs.next()) { - return rs.getLong(0); + }, + executor); + CommitTimestampFuture commitTimestamp = updateCount.commitAsync(); + try { + assertThat(updateCount.get()).isEqualTo(UPDATE_COUNT); + assertThat(commitTimestamp.get()).isNotNull(); + break; + } catch (AbortedException e) { + txn = txMgr.resetForRetryAsync(); + } + } + } + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countTransactionsStarted()).isEqualTo(1); + } + + @Test + public void testAsyncTransactionManagerInlinedBeginTxAborted() + throws InterruptedException, ExecutionException { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + try (AsyncTransactionManager txMgr = client.transactionManagerAsync()) { + TransactionContextFuture txn = txMgr.beginAsync(); + boolean first = true; + while (true) { + try { + AsyncTransactionStep updateCount = + txn.then( + new AsyncTransactionFunction() { + @Override + public ApiFuture apply(TransactionContext txn, Void input) + throws Exception { + return txn.executeUpdateAsync(UPDATE_STATEMENT); } + }, + executor); + if (first) { + // Abort the transaction after the statement has been executed to ensure that the + // transaction has actually been started before the test tries to abort it. + updateCount.then( + new AsyncTransactionFunction() { + @Override + public ApiFuture apply(TransactionContext txn, Long input) + throws Exception { + mockSpanner.abortAllTransactions(); + return ApiFutures.immediateFuture(null); } - return 0L; - } - }); - assertThat(updateCount).isEqualTo(1L); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(2); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + }, + MoreExecutors.directExecutor()); + first = false; + } + assertThat(updateCount.commitAsync().get()).isNotNull(); + assertThat(updateCount.get()).isEqualTo(UPDATE_COUNT); + break; + } catch (AbortedException e) { + txn = txMgr.resetForRetryAsync(); + } + } + } + // The retry will use a BeginTransaction RPC. + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); + assertThat(countTransactionsStarted()).isEqualTo(2); + } + + @Test + public void testAsyncTransactionManagerInlinedBeginTxWithOnlyMutations() + throws InterruptedException, ExecutionException { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + try (AsyncTransactionManager txMgr = client.transactionManagerAsync()) { + TransactionContextFuture txn = txMgr.beginAsync(); + while (true) { + try { + txn.then( + new AsyncTransactionFunction() { + @Override + public ApiFuture apply(TransactionContext txn, Void input) + throws Exception { + txn.buffer(Mutation.newInsertBuilder("FOO").set("ID").to(1L).build()); + return ApiFutures.immediateFuture(null); + } + }, + executor) + .commitAsync() + .get(); + break; + } catch (AbortedException e) { + txn = txMgr.resetForRetryAsync(); + } + } + } + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + assertThat(countTransactionsStarted()).isEqualTo(1); + } + + @Test + public void testAsyncTransactionManagerInlinedBeginTxWithError() + throws InterruptedException, ExecutionException { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + try (AsyncTransactionManager txMgr = client.transactionManagerAsync()) { + TransactionContextFuture txn = txMgr.beginAsync(); + while (true) { + try { + AsyncTransactionStep updateCount = + txn.then( + new AsyncTransactionFunction() { + @Override + public ApiFuture apply(TransactionContext txn, Void input) + throws Exception { + return txn.executeUpdateAsync(INVALID_UPDATE_STATEMENT); + } + }, + executor) + .then( + new AsyncTransactionFunction() { + @Override + public ApiFuture apply(TransactionContext txn, Long input) + throws Exception { + return txn.executeUpdateAsync(UPDATE_STATEMENT); + } + }, + executor); + try { + updateCount.commitAsync().get(); + fail("missing expected exception"); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(SpannerException.class); + SpannerException se = (SpannerException) e.getCause(); + assertThat(se.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + } + break; + } catch (AbortedException e) { + txn = txMgr.resetForRetryAsync(); + } + } + } + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countTransactionsStarted()).isEqualTo(1); + } } - @Test - public void testInlinedBeginFirstQueryReturnsUnavailable() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - mockSpanner.setExecuteStreamingSqlExecutionTime( - SimulatedExecutionTime.ofStreamException(Status.UNAVAILABLE.asRuntimeException(), 0)); - long value = - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(TransactionContext transaction) throws Exception { - // The first attempt will return UNAVAILABLE and retry internally. - try (ResultSet rs = transaction.executeQuery(SELECT1)) { - while (rs.next()) { - return rs.getLong(0); + @RunWith(JUnit4.class) + public static class InlineBeginTransactionWithoutExecutorTest extends InlineBeginTransactionTest { + @Test + public void testInlinedBeginTx() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + long updateCount = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Long run(TransactionContext transaction) throws Exception { + return transaction.executeUpdate(UPDATE_STATEMENT); + } + }); + assertThat(updateCount).isEqualTo(UPDATE_COUNT); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + assertThat(countTransactionsStarted()).isEqualTo(1); + } + + @Test + public void testInlinedBeginTxAborted() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + final AtomicBoolean firstAttempt = new AtomicBoolean(true); + long updateCount = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Long run(TransactionContext transaction) throws Exception { + long res = transaction.executeUpdate(UPDATE_STATEMENT); + if (firstAttempt.getAndSet(false)) { + mockSpanner.abortTransaction(transaction); } + return res; } - return 0L; - } - }); - assertThat(value).isEqualTo(1L); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(2); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - } + }); + assertThat(updateCount).isEqualTo(UPDATE_COUNT); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(2); + // We have started 2 transactions, because the first transaction aborted during the commit. + assertThat(countRequests(CommitRequest.class)).isEqualTo(2); + assertThat(countTransactionsStarted()).isEqualTo(2); + } - @Test - public void testInlinedBeginFirstReadReturnsUnavailable() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - mockSpanner.setStreamingReadExecutionTime( - SimulatedExecutionTime.ofStreamException(Status.UNAVAILABLE.asRuntimeException(), 0)); - long value = - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(TransactionContext transaction) throws Exception { - // The first attempt will return UNAVAILABLE and retry internally. - try (ResultSet rs = - transaction.read("FOO", KeySet.all(), Arrays.asList("ID"))) { - while (rs.next()) { - return rs.getLong(0); + @Test + public void testInlinedBeginFirstUpdateAborts() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + long updateCount = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + boolean firstAttempt = true; + + @Override + public Long run(TransactionContext transaction) throws Exception { + if (firstAttempt) { + firstAttempt = false; + mockSpanner.putStatementResult( + StatementResult.exception( + UPDATE_STATEMENT, + mockSpanner.createAbortedException( + ByteString.copyFromUtf8("some-tx")))); + } else { + mockSpanner.putStatementResult( + StatementResult.update(UPDATE_STATEMENT, UPDATE_COUNT)); } + return transaction.executeUpdate(UPDATE_STATEMENT); } - return 0L; - } - }); - assertThat(value).isEqualTo(1L); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ReadRequest.class)).isEqualTo(2); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - } + }); + assertThat(updateCount).isEqualTo(UPDATE_COUNT); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(2); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + } - @Test - public void testInlinedBeginTxWithQuery() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - long updateCount = - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(TransactionContext transaction) throws Exception { - try (ResultSet rs = transaction.executeQuery(SELECT1)) { - while (rs.next()) { - return rs.getLong(0); + @Test + public void testInlinedBeginFirstQueryAborts() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + long updateCount = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + boolean firstAttempt = true; + + @Override + public Long run(TransactionContext transaction) throws Exception { + if (firstAttempt) { + firstAttempt = false; + mockSpanner.putStatementResult( + StatementResult.exception( + SELECT1, + mockSpanner.createAbortedException( + ByteString.copyFromUtf8("some-tx")))); + } else { + mockSpanner.putStatementResult( + StatementResult.query(SELECT1, SELECT1_RESULTSET)); + } + try (ResultSet rs = transaction.executeQuery(SELECT1)) { + while (rs.next()) { + return rs.getLong(0); + } } + return 0L; } - return 0L; - } - }); - assertThat(updateCount).isEqualTo(1L); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - assertThat(countTransactionsStarted()).isEqualTo(1); - } + }); + assertThat(updateCount).isEqualTo(1L); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(2); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + } + + @Test + public void testInlinedBeginFirstQueryReturnsUnavailable() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + mockSpanner.setExecuteStreamingSqlExecutionTime( + SimulatedExecutionTime.ofStreamException(Status.UNAVAILABLE.asRuntimeException(), 0)); + long value = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Long run(TransactionContext transaction) throws Exception { + // The first attempt will return UNAVAILABLE and retry internally. + try (ResultSet rs = transaction.executeQuery(SELECT1)) { + while (rs.next()) { + return rs.getLong(0); + } + } + return 0L; + } + }); + assertThat(value).isEqualTo(1L); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(2); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + } + + @Test + public void testInlinedBeginFirstReadReturnsUnavailable() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + mockSpanner.setStreamingReadExecutionTime( + SimulatedExecutionTime.ofStreamException(Status.UNAVAILABLE.asRuntimeException(), 0)); + long value = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Long run(TransactionContext transaction) throws Exception { + // The first attempt will return UNAVAILABLE and retry internally. + try (ResultSet rs = + transaction.read("FOO", KeySet.all(), Arrays.asList("ID"))) { + while (rs.next()) { + return rs.getLong(0); + } + } + return 0L; + } + }); + assertThat(value).isEqualTo(1L); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ReadRequest.class)).isEqualTo(2); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + } + + @Test + public void testInlinedBeginTxWithQuery() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + long updateCount = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Long run(TransactionContext transaction) throws Exception { + try (ResultSet rs = transaction.executeQuery(SELECT1)) { + while (rs.next()) { + return rs.getLong(0); + } + } + return 0L; + } + }); + assertThat(updateCount).isEqualTo(1L); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + assertThat(countTransactionsStarted()).isEqualTo(1); + } + + @Test + public void testInlinedBeginTxWithRead() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + long updateCount = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Long run(TransactionContext transaction) throws Exception { + try (ResultSet rs = + transaction.read("FOO", KeySet.all(), Arrays.asList("ID"))) { + while (rs.next()) { + return rs.getLong(0); + } + } + return 0L; + } + }); + assertThat(updateCount).isEqualTo(1L); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ReadRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + assertThat(countTransactionsStarted()).isEqualTo(1); + } + + @Test + public void testInlinedBeginTxWithBatchDml() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + long[] updateCounts = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public long[] run(TransactionContext transaction) throws Exception { + return transaction.batchUpdate( + Arrays.asList(UPDATE_STATEMENT, UPDATE_STATEMENT)); + } + }); + assertThat(updateCounts).asList().containsExactly(UPDATE_COUNT, UPDATE_COUNT); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteBatchDmlRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + assertThat(countTransactionsStarted()).isEqualTo(1); + } + + @Test + public void testInlinedBeginTxWithError() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + long updateCount = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Long run(TransactionContext transaction) throws Exception { + try { + transaction.executeUpdate(INVALID_UPDATE_STATEMENT); + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + } + return transaction.executeUpdate(UPDATE_STATEMENT); + } + }); + assertThat(updateCount).isEqualTo(UPDATE_COUNT); + // The transaction will be retried because the first statement that also tried to include the + // BeginTransaction statement failed and did not return a transaction. That forces a retry of + // the entire transaction with an explicit BeginTransaction RPC. + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); + // The update statement will be executed 3 times: + // 1. The invalid update statement will be executed during the first attempt and fail. The + // second update statement will not be executed, as the transaction runner sees that the + // initial + // statement failed and did not return a valid transaction id. + // 2. The invalid update statement is executed again during the retry. + // 3. The valid update statement is only executed after the first statement succeeded. + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(3); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + // The first update will start a transaction, but then fail the update statement. This will + // start a transaction on the mock server, but that transaction will never be returned to the + // client. + assertThat(countTransactionsStarted()).isEqualTo(2); + } - @Test - public void testInlinedBeginTxWithRead() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - long updateCount = + @Test + public void testInlinedBeginTxWithErrorOnFirstStatement_andThenErrorOnBeginTransaction() { + mockSpanner.setBeginTransactionExecutionTime( + SimulatedExecutionTime.ofException( + Status.INTERNAL + .withDescription("Begin transaction failed due to an internal error") + .asRuntimeException())); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + try { client .readWriteTransaction() .run( - new TransactionCallable() { + new TransactionCallable() { @Override - public Long run(TransactionContext transaction) throws Exception { - try (ResultSet rs = - transaction.read("FOO", KeySet.all(), Arrays.asList("ID"))) { - while (rs.next()) { - return rs.getLong(0); - } + public Void run(TransactionContext transaction) throws Exception { + try { + transaction.executeUpdate(INVALID_UPDATE_STATEMENT); + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); } - return 0L; + return null; } }); - assertThat(updateCount).isEqualTo(1L); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ReadRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - assertThat(countTransactionsStarted()).isEqualTo(1); - } + fail("Missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INTERNAL); + assertThat(e.getMessage()).contains("Begin transaction failed due to an internal error"); + } + // The transaction will be retried because the first statement that also tried to include the + // BeginTransaction statement failed and did not return a transaction. That forces a retry of + // the entire transaction with an explicit BeginTransaction RPC. + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(0); + // The explicit BeginTransaction RPC failed, so only one transaction was started. + assertThat(countTransactionsStarted()).isEqualTo(1); + } - @Test - public void testInlinedBeginTxWithBatchDml() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - long[] updateCounts = + @Test + public void testInlinedBeginTxWithUncaughtError() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + try { client .readWriteTransaction() .run( - new TransactionCallable() { + new TransactionCallable() { @Override - public long[] run(TransactionContext transaction) throws Exception { - return transaction.batchUpdate( - Arrays.asList(UPDATE_STATEMENT, UPDATE_STATEMENT)); + public Long run(TransactionContext transaction) throws Exception { + return transaction.executeUpdate(INVALID_UPDATE_STATEMENT); } }); - assertThat(updateCounts).asList().containsExactly(UPDATE_COUNT, UPDATE_COUNT); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteBatchDmlRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - assertThat(countTransactionsStarted()).isEqualTo(1); - } + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + } + // The first update will start a transaction, but then fail the update statement. This will + // start a transaction on the mock server, but that transaction will never be returned to the + // client. + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(CommitRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); + // No rollback request will be initiated because the client does not receive any transaction + // id. + assertThat(countRequests(RollbackRequest.class)).isEqualTo(0); + assertThat(countTransactionsStarted()).isEqualTo(1); + } - @Test - public void testInlinedBeginTxWithError() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - long updateCount = + @Test + public void testInlinedBeginTxWithUncaughtErrorAfterSuccessfulBegin() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + try { client .readWriteTransaction() .run( new TransactionCallable() { @Override public Long run(TransactionContext transaction) throws Exception { - try { - transaction.executeUpdate(INVALID_UPDATE_STATEMENT); - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); - } - return transaction.executeUpdate(UPDATE_STATEMENT); + // This statement will start a transaction. + transaction.executeUpdate(UPDATE_STATEMENT); + // This statement will fail and cause a rollback as the exception is not caught. + return transaction.executeUpdate(INVALID_UPDATE_STATEMENT); } }); - assertThat(updateCount).isEqualTo(UPDATE_COUNT); - // The transaction will be retried because the first statement that also tried to include the - // BeginTransaction statement failed and did not return a transaction. That forces a retry of - // the entire transaction with an explicit BeginTransaction RPC. - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); - // The update statement will be executed 3 times: - // 1. The invalid update statement will be executed during the first attempt and fail. The - // second update statement will not be executed, as the transaction runner sees that the initial - // statement failed and did not return a valid transaction id. - // 2. The invalid update statement is executed again during the retry. - // 3. The valid update statement is only executed after the first statement succeeded. - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(3); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - // The first update will start a transaction, but then fail the update statement. This will - // start a transaction on the mock server, but that transaction will never be returned to the - // client. - assertThat(countTransactionsStarted()).isEqualTo(2); - } + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + } + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(CommitRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(2); + assertThat(countRequests(RollbackRequest.class)).isEqualTo(1); + assertThat(countTransactionsStarted()).isEqualTo(1); + } - @Test - public void testInlinedBeginTxWithErrorOnFirstStatement_andThenErrorOnBeginTransaction() { - mockSpanner.setBeginTransactionExecutionTime( - SimulatedExecutionTime.ofException( - Status.INTERNAL - .withDescription("Begin transaction failed due to an internal error") - .asRuntimeException())); - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - try { - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Void run(TransactionContext transaction) throws Exception { - try { - transaction.executeUpdate(INVALID_UPDATE_STATEMENT); - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); - } - return null; - } - }); - fail("Missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INTERNAL); - assertThat(e.getMessage()).contains("Begin transaction failed due to an internal error"); + @Test + public void testInlinedBeginTxBatchDmlWithErrorOnFirstStatement() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + Void res = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + try { + transaction.batchUpdate( + ImmutableList.of(INVALID_UPDATE_STATEMENT, UPDATE_STATEMENT)); + fail("missing expected exception"); + } catch (SpannerBatchUpdateException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + assertThat(e.getUpdateCounts()).hasLength(0); + } + return null; + } + }); + assertThat(res).isNull(); + // The first statement failed and could not return a transaction. The entire transaction is + // therefore retried with an explicit BeginTransaction RPC. + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); + assertThat(countRequests(ExecuteBatchDmlRequest.class)).isEqualTo(2); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + assertThat(countTransactionsStarted()).isEqualTo(2); } - // The transaction will be retried because the first statement that also tried to include the - // BeginTransaction statement failed and did not return a transaction. That forces a retry of - // the entire transaction with an explicit BeginTransaction RPC. - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(0); - // The explicit BeginTransaction RPC failed, so only one transaction was started. - assertThat(countTransactionsStarted()).isEqualTo(1); - } - @Test - public void testInlinedBeginTxWithUncaughtError() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - try { - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(TransactionContext transaction) throws Exception { - return transaction.executeUpdate(INVALID_UPDATE_STATEMENT); - } - }); - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); - } - // The first update will start a transaction, but then fail the update statement. This will - // start a transaction on the mock server, but that transaction will never be returned to the - // client. - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(CommitRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); - // No rollback request will be initiated because the client does not receive any transaction id. - assertThat(countRequests(RollbackRequest.class)).isEqualTo(0); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - - @Test - public void testInlinedBeginTxWithUncaughtErrorAfterSuccessfulBegin() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - try { - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(TransactionContext transaction) throws Exception { - // This statement will start a transaction. - transaction.executeUpdate(UPDATE_STATEMENT); - // This statement will fail and cause a rollback as the exception is not caught. - return transaction.executeUpdate(INVALID_UPDATE_STATEMENT); - } - }); - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); - } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(CommitRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(2); - assertThat(countRequests(RollbackRequest.class)).isEqualTo(1); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - - @Test - public void testInlinedBeginTxBatchDmlWithErrorOnFirstStatement() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - Void res = - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Void run(TransactionContext transaction) throws Exception { - try { - transaction.batchUpdate( - ImmutableList.of(INVALID_UPDATE_STATEMENT, UPDATE_STATEMENT)); - fail("missing expected exception"); - } catch (SpannerBatchUpdateException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); - assertThat(e.getUpdateCounts()).hasLength(0); - } - return null; - } - }); - assertThat(res).isNull(); - // The first statement failed and could not return a transaction. The entire transaction is - // therefore retried with an explicit BeginTransaction RPC. - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); - assertThat(countRequests(ExecuteBatchDmlRequest.class)).isEqualTo(2); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - assertThat(countTransactionsStarted()).isEqualTo(2); - } - - @Test - public void testInlinedBeginTxBatchDmlWithErrorOnSecondStatement() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - long updateCount = - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(TransactionContext transaction) throws Exception { - try { - transaction.batchUpdate( - ImmutableList.of(UPDATE_STATEMENT, INVALID_UPDATE_STATEMENT)); - fail("missing expected exception"); - // The following line is needed as the compiler does not know that this is - // unreachable. - return -1L; - } catch (SpannerBatchUpdateException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); - assertThat(e.getUpdateCounts()).hasLength(1); - return e.getUpdateCounts()[0]; + @Test + public void testInlinedBeginTxBatchDmlWithErrorOnSecondStatement() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + long updateCount = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Long run(TransactionContext transaction) throws Exception { + try { + transaction.batchUpdate( + ImmutableList.of(UPDATE_STATEMENT, INVALID_UPDATE_STATEMENT)); + fail("missing expected exception"); + // The following line is needed as the compiler does not know that this is + // unreachable. + return -1L; + } catch (SpannerBatchUpdateException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + assertThat(e.getUpdateCounts()).hasLength(1); + return e.getUpdateCounts()[0]; + } } - } - }); - assertThat(updateCount).isEqualTo(UPDATE_COUNT); - // Although the batch DML returned an error, that error was for the second statement. That means - // that the transaction was started by the first statement. - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteBatchDmlRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - assertThat(countTransactionsStarted()).isEqualTo(1); - } + }); + assertThat(updateCount).isEqualTo(UPDATE_COUNT); + // Although the batch DML returned an error, that error was for the second statement. That + // means + // that the transaction was started by the first statement. + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteBatchDmlRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + assertThat(countTransactionsStarted()).isEqualTo(1); + } - @Test - public void testInlinedBeginTxWithErrorOnStreamingSql() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - Void res = - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Void run(TransactionContext transaction) throws Exception { - try (ResultSet rs = transaction.executeQuery(INVALID_SELECT)) { - while (rs.next()) {} - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + @Test + public void testInlinedBeginTxWithErrorOnStreamingSql() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + Void res = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + try (ResultSet rs = transaction.executeQuery(INVALID_SELECT)) { + while (rs.next()) {} + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + } + return null; } - return null; - } - }); - assertThat(res).isNull(); - // The transaction will be retried because the first statement that also tried to include the - // BeginTransaction statement failed and did not return a transaction. That forces a retry of - // the entire transaction with an explicit BeginTransaction RPC. - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(2); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - // The first update will start a transaction, but then fail the update statement. This will - // start a transaction on the mock server, but that transaction will never be returned to the - // client. - assertThat(countTransactionsStarted()).isEqualTo(2); - } + }); + assertThat(res).isNull(); + // The transaction will be retried because the first statement that also tried to include the + // BeginTransaction statement failed and did not return a transaction. That forces a retry of + // the entire transaction with an explicit BeginTransaction RPC. + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(2); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + // The first update will start a transaction, but then fail the update statement. This will + // start a transaction on the mock server, but that transaction will never be returned to the + // client. + assertThat(countTransactionsStarted()).isEqualTo(2); + } - @Test - public void testInlinedBeginTxWithErrorOnSecondPartialResultSet() { - final Statement statement = Statement.of("SELECT * FROM BROKEN_TABLE"); - RandomResultSetGenerator generator = new RandomResultSetGenerator(2); - mockSpanner.putStatementResult(StatementResult.query(statement, generator.generate())); - // The first PartialResultSet will be returned successfully, and then a DATA_LOSS exception will - // be returned. - mockSpanner.setExecuteStreamingSqlExecutionTime( - SimulatedExecutionTime.ofStreamException(Status.DATA_LOSS.asRuntimeException(), 1)); - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - Void res = - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Void run(TransactionContext transaction) throws Exception { - try (ResultSet rs = transaction.executeQuery(statement)) { - while (rs.next()) {} - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.DATA_LOSS); + @Test + public void testInlinedBeginTxWithErrorOnSecondPartialResultSet() { + final Statement statement = Statement.of("SELECT * FROM BROKEN_TABLE"); + RandomResultSetGenerator generator = new RandomResultSetGenerator(2); + mockSpanner.putStatementResult(StatementResult.query(statement, generator.generate())); + // The first PartialResultSet will be returned successfully, and then a DATA_LOSS exception + // will + // be returned. + mockSpanner.setExecuteStreamingSqlExecutionTime( + SimulatedExecutionTime.ofStreamException(Status.DATA_LOSS.asRuntimeException(), 1)); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + Void res = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + try (ResultSet rs = transaction.executeQuery(statement)) { + while (rs.next()) {} + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.DATA_LOSS); + } + return null; } - return null; - } - }); - assertThat(res).isNull(); - // The transaction will not be retried, as the first PartialResultSet returns the transaction - // ID, and the second fails with an error code. - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - assertThat(countTransactionsStarted()).isEqualTo(1); - } + }); + assertThat(res).isNull(); + // The transaction will not be retried, as the first PartialResultSet returns the transaction + // ID, and the second fails with an error code. + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + assertThat(countTransactionsStarted()).isEqualTo(1); + } - @Test - public void testInlinedBeginTxWithParallelQueries() { - final int numQueries = 100; - final ScheduledExecutorService executor = Executors.newScheduledThreadPool(16); - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - long updateCount = - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(final TransactionContext transaction) throws Exception { - List> futures = new ArrayList<>(numQueries); - for (int i = 0; i < numQueries; i++) { - futures.add( - executor.submit( - new Callable() { - @Override - public Long call() throws Exception { - try (ResultSet rs = transaction.executeQuery(SELECT1)) { - while (rs.next()) { - return rs.getLong(0); + @Test + public void testInlinedBeginTxWithParallelQueries() { + final int numQueries = 100; + final ScheduledExecutorService executor = Executors.newScheduledThreadPool(16); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + long updateCount = + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Long run(final TransactionContext transaction) throws Exception { + List> futures = new ArrayList<>(numQueries); + for (int i = 0; i < numQueries; i++) { + futures.add( + executor.submit( + new Callable() { + @Override + public Long call() throws Exception { + try (ResultSet rs = transaction.executeQuery(SELECT1)) { + while (rs.next()) { + return rs.getLong(0); + } } + return 0L; } - return 0L; - } - })); - } - Long res = 0L; - for (Future f : futures) { - res += f.get(); + })); + } + Long res = 0L; + for (Future f : futures) { + res += f.get(); + } + return res; } - return res; - } - }); - assertThat(updateCount).isEqualTo(1L * numQueries); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countTransactionsStarted()).isEqualTo(1); - } + }); + assertThat(updateCount).isEqualTo(1L * numQueries); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countTransactionsStarted()).isEqualTo(1); + } - @Test - public void testInlinedBeginTxWithOnlyMutations() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Void run(TransactionContext transaction) throws Exception { - transaction.buffer( - Arrays.asList( - Mutation.newInsertBuilder("FOO").set("ID").to(1L).build(), - Mutation.delete("FOO", Key.of(1L)))); - return null; - } - }); - // There should be 1 call to BeginTransaction because there is no statement that we can use to - // inline the BeginTransaction call with. - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - assertThat(countTransactionsStarted()).isEqualTo(1); - } + @Test + public void testInlinedBeginTxWithOnlyMutations() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + transaction.buffer( + Arrays.asList( + Mutation.newInsertBuilder("FOO").set("ID").to(1L).build(), + Mutation.delete("FOO", Key.of(1L)))); + return null; + } + }); + // There should be 1 call to BeginTransaction because there is no statement that we can use to + // inline the BeginTransaction call with. + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + assertThat(countTransactionsStarted()).isEqualTo(1); + } - @SuppressWarnings("resource") - @Test - public void testTransactionManagerInlinedBeginTx() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - try (TransactionManager txMgr = client.transactionManager()) { - TransactionContext txn = txMgr.begin(); - while (true) { - try { - assertThat(txn.executeUpdate(UPDATE_STATEMENT)).isEqualTo(UPDATE_COUNT); - txMgr.commit(); - break; - } catch (AbortedException e) { - txn = txMgr.resetForRetry(); + @SuppressWarnings("resource") + @Test + public void testTransactionManagerInlinedBeginTx() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + try (TransactionManager txMgr = client.transactionManager()) { + TransactionContext txn = txMgr.begin(); + while (true) { + try { + assertThat(txn.executeUpdate(UPDATE_STATEMENT)).isEqualTo(UPDATE_COUNT); + txMgr.commit(); + break; + } catch (AbortedException e) { + txn = txMgr.resetForRetry(); + } } } + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countTransactionsStarted()).isEqualTo(1); } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - @SuppressWarnings("resource") - @Test - public void testTransactionManagerInlinedBeginTxAborted() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - try (TransactionManager txMgr = client.transactionManager()) { - TransactionContext txn = txMgr.begin(); - boolean first = true; - while (true) { - try { - assertThat(txn.executeUpdate(UPDATE_STATEMENT)).isEqualTo(UPDATE_COUNT); - if (first) { - mockSpanner.abortAllTransactions(); - first = false; + @SuppressWarnings("resource") + @Test + public void testTransactionManagerInlinedBeginTxAborted() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + try (TransactionManager txMgr = client.transactionManager()) { + TransactionContext txn = txMgr.begin(); + boolean first = true; + while (true) { + try { + assertThat(txn.executeUpdate(UPDATE_STATEMENT)).isEqualTo(UPDATE_COUNT); + if (first) { + mockSpanner.abortAllTransactions(); + first = false; + } + txMgr.commit(); + break; + } catch (AbortedException e) { + txn = txMgr.resetForRetry(); } - txMgr.commit(); - break; - } catch (AbortedException e) { - txn = txMgr.resetForRetry(); } } + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countTransactionsStarted()).isEqualTo(2); } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countTransactionsStarted()).isEqualTo(2); - } - @SuppressWarnings("resource") - @Test - public void testTransactionManagerInlinedBeginTxWithOnlyMutations() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - try (TransactionManager txMgr = client.transactionManager()) { - TransactionContext txn = txMgr.begin(); - while (true) { - try { - txn.buffer(Mutation.newInsertBuilder("FOO").set("ID").to(1L).build()); - txMgr.commit(); - break; - } catch (AbortedException e) { - txn = txMgr.resetForRetry(); + @SuppressWarnings("resource") + @Test + public void testTransactionManagerInlinedBeginTxWithOnlyMutations() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + try (TransactionManager txMgr = client.transactionManager()) { + TransactionContext txn = txMgr.begin(); + while (true) { + try { + txn.buffer(Mutation.newInsertBuilder("FOO").set("ID").to(1L).build()); + txMgr.commit(); + break; + } catch (AbortedException e) { + txn = txMgr.resetForRetry(); + } } } + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + assertThat(countTransactionsStarted()).isEqualTo(1); } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - @SuppressWarnings("resource") - @Test - public void testTransactionManagerInlinedBeginTxWithError() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - try (TransactionManager txMgr = client.transactionManager()) { - TransactionContext txn = txMgr.begin(); - while (true) { - try { + @SuppressWarnings("resource") + @Test + public void testTransactionManagerInlinedBeginTxWithError() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + try (TransactionManager txMgr = client.transactionManager()) { + TransactionContext txn = txMgr.begin(); + while (true) { try { - txn.executeUpdate(INVALID_UPDATE_STATEMENT); - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + try { + txn.executeUpdate(INVALID_UPDATE_STATEMENT); + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + } + assertThat(txn.executeUpdate(UPDATE_STATEMENT)).isEqualTo(UPDATE_COUNT); + txMgr.commit(); + break; + } catch (AbortedException e) { + txn = txMgr.resetForRetry(); } - assertThat(txn.executeUpdate(UPDATE_STATEMENT)).isEqualTo(UPDATE_COUNT); - txMgr.commit(); - break; - } catch (AbortedException e) { - txn = txMgr.resetForRetry(); } } + // The first statement will fail and not return a transaction id. This will trigger a retry of + // the entire transaction, and the retry will do an explicit BeginTransaction RPC. + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); + // The first statement will start a transaction, but it will never be returned to the client + // as + // the update statement fails. + assertThat(countTransactionsStarted()).isEqualTo(2); } - // The first statement will fail and not return a transaction id. This will trigger a retry of - // the entire transaction, and the retry will do an explicit BeginTransaction RPC. - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); - // The first statement will start a transaction, but it will never be returned to the client as - // the update statement fails. - assertThat(countTransactionsStarted()).isEqualTo(2); - } - @SuppressWarnings("resource") - @Test - public void testTransactionManagerInlinedBeginTxWithUncaughtError() { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - try (TransactionManager txMgr = client.transactionManager()) { - TransactionContext txn = txMgr.begin(); - while (true) { - try { - txn.executeUpdate(INVALID_UPDATE_STATEMENT); - fail("missing expected exception"); - } catch (AbortedException e) { - txn = txMgr.resetForRetry(); + @SuppressWarnings("resource") + @Test + public void testTransactionManagerInlinedBeginTxWithUncaughtError() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + try (TransactionManager txMgr = client.transactionManager()) { + TransactionContext txn = txMgr.begin(); + while (true) { + try { + txn.executeUpdate(INVALID_UPDATE_STATEMENT); + fail("missing expected exception"); + } catch (AbortedException e) { + txn = txMgr.resetForRetry(); + } } + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); } - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countTransactionsStarted()).isEqualTo(1); } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - - @Test - public void testInlinedBeginAsyncTx() throws InterruptedException, ExecutionException { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - ApiFuture updateCount = - client - .runAsync() - .runAsync( - new AsyncWork() { - @Override - public ApiFuture doWorkAsync(TransactionContext txn) { - return txn.executeUpdateAsync(UPDATE_STATEMENT); - } - }, - executor); - assertThat(updateCount.get()).isEqualTo(UPDATE_COUNT); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - - @Test - public void testInlinedBeginAsyncTxAborted() throws InterruptedException, ExecutionException { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - final AtomicBoolean firstAttempt = new AtomicBoolean(true); - ApiFuture updateCount = - client - .runAsync() - .runAsync( - new AsyncWork() { - @Override - public ApiFuture doWorkAsync(TransactionContext txn) { - ApiFuture res = txn.executeUpdateAsync(UPDATE_STATEMENT); - if (firstAttempt.getAndSet(false)) { - mockSpanner.abortTransaction(txn); - } - return res; - } - }, - executor); - assertThat(updateCount.get()).isEqualTo(UPDATE_COUNT); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - // We have started 2 transactions, because the first transaction aborted. - assertThat(countTransactionsStarted()).isEqualTo(2); - } - @Test - public void testInlinedBeginAsyncTxWithQuery() throws InterruptedException, ExecutionException { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - final ExecutorService queryExecutor = Executors.newSingleThreadExecutor(); - ApiFuture updateCount = - client - .runAsync() - .runAsync( - new AsyncWork() { - @Override - public ApiFuture doWorkAsync(TransactionContext txn) { - final SettableApiFuture res = SettableApiFuture.create(); - try (AsyncResultSet rs = txn.executeQueryAsync(SELECT1)) { - rs.setCallback( - executor, - new ReadyCallback() { - @Override - public CallbackResponse cursorReady(AsyncResultSet resultSet) { - switch (resultSet.tryNext()) { - case DONE: - return CallbackResponse.DONE; - case NOT_READY: - return CallbackResponse.CONTINUE; - case OK: - res.set(resultSet.getLong(0)); - default: - throw new IllegalStateException(); - } - } - }); - } - return res; - } - }, - queryExecutor); - assertThat(updateCount.get()).isEqualTo(1L); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countTransactionsStarted()).isEqualTo(1); - queryExecutor.shutdown(); - } - - @Test - public void testInlinedBeginAsyncTxWithBatchDml() - throws InterruptedException, ExecutionException { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - ApiFuture updateCounts = - client - .runAsync() - .runAsync( - new AsyncWork() { - @Override - public ApiFuture doWorkAsync(TransactionContext transaction) { - return transaction.batchUpdateAsync( - Arrays.asList(UPDATE_STATEMENT, UPDATE_STATEMENT)); - } - }, - executor); - assertThat(updateCounts.get()).asList().containsExactly(UPDATE_COUNT, UPDATE_COUNT); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - - @Test - public void testInlinedBeginAsyncTxWithError() throws InterruptedException, ExecutionException { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - ApiFuture updateCount = - client - .runAsync() - .runAsync( - new AsyncWork() { - @Override - public ApiFuture doWorkAsync(TransactionContext transaction) { - transaction.executeUpdateAsync(INVALID_UPDATE_STATEMENT); - return transaction.executeUpdateAsync(UPDATE_STATEMENT); - } - }, - executor); - assertThat(updateCount.get()).isEqualTo(UPDATE_COUNT); - // The first statement will fail and not return a transaction id. This will trigger a retry of - // the entire transaction, and the retry will do an explicit BeginTransaction RPC. - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); - // The first update will start a transaction, but then fail the update statement. This will - // start a transaction on the mock server, but that transaction will never be returned to the - // client. - assertThat(countTransactionsStarted()).isEqualTo(2); - } - - @Test - public void testInlinedBeginAsyncTxWithParallelQueries() - throws InterruptedException, ExecutionException { - final int numQueries = 100; - final ScheduledExecutorService executor = Executors.newScheduledThreadPool(16); - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - ApiFuture updateCount = - client - .runAsync() - .runAsync( - new AsyncWork() { - @Override - public ApiFuture doWorkAsync(final TransactionContext txn) { - List> futures = new ArrayList<>(numQueries); - for (int i = 0; i < numQueries; i++) { - final SettableApiFuture res = SettableApiFuture.create(); - try (AsyncResultSet rs = txn.executeQueryAsync(SELECT1)) { - rs.setCallback( - executor, - new ReadyCallback() { - @Override - public CallbackResponse cursorReady(AsyncResultSet resultSet) { - switch (resultSet.tryNext()) { - case DONE: - return CallbackResponse.DONE; - case NOT_READY: - return CallbackResponse.CONTINUE; - case OK: - res.set(resultSet.getLong(0)); - default: - throw new IllegalStateException(); + @Test + public void testInlinedBeginAsyncTxWithParallelQueries() + throws InterruptedException, ExecutionException { + final int numQueries = 100; + final ScheduledExecutorService executor = Executors.newScheduledThreadPool(16); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + ApiFuture updateCount = + client + .runAsync() + .runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(final TransactionContext txn) { + List> futures = new ArrayList<>(numQueries); + for (int i = 0; i < numQueries; i++) { + final SettableApiFuture res = SettableApiFuture.create(); + try (AsyncResultSet rs = txn.executeQueryAsync(SELECT1)) { + rs.setCallback( + executor, + new ReadyCallback() { + @Override + public CallbackResponse cursorReady(AsyncResultSet resultSet) { + switch (resultSet.tryNext()) { + case DONE: + return CallbackResponse.DONE; + case NOT_READY: + return CallbackResponse.CONTINUE; + case OK: + res.set(resultSet.getLong(0)); + default: + throw new IllegalStateException(); + } } - } - }); + }); + } + futures.add(res); } - futures.add(res); - } - return ApiFutures.transformAsync( - ApiFutures.allAsList(futures), - new ApiAsyncFunction, Long>() { - @Override - public ApiFuture apply(List input) throws Exception { - long sum = 0L; - for (Long l : input) { - sum += l; + return ApiFutures.transformAsync( + ApiFutures.allAsList(futures), + new ApiAsyncFunction, Long>() { + @Override + public ApiFuture apply(List input) throws Exception { + long sum = 0L; + for (Long l : input) { + sum += l; + } + return ApiFutures.immediateFuture(sum); } - return ApiFutures.immediateFuture(sum); - } - }, - MoreExecutors.directExecutor()); - } - }, - executor); - assertThat(updateCount.get()).isEqualTo(1L * numQueries); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - - @Test - public void testInlinedBeginAsyncTxWithOnlyMutations() - throws InterruptedException, ExecutionException { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - client - .runAsync() - .runAsync( - new AsyncWork() { - @Override - public ApiFuture doWorkAsync(TransactionContext transaction) { - transaction.buffer(Mutation.newInsertBuilder("FOO").set("ID").to(1L).build()); - return ApiFutures.immediateFuture(null); - } - }, - executor) - .get(); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - - @Test - public void testAsyncTransactionManagerInlinedBeginTx() - throws InterruptedException, ExecutionException { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - try (AsyncTransactionManager txMgr = client.transactionManagerAsync()) { - TransactionContextFuture txn = txMgr.beginAsync(); - while (true) { - AsyncTransactionStep updateCount = - txn.then( - new AsyncTransactionFunction() { - @Override - public ApiFuture apply(TransactionContext txn, Void input) - throws Exception { - return txn.executeUpdateAsync(UPDATE_STATEMENT); - } - }, - executor); - CommitTimestampFuture commitTimestamp = updateCount.commitAsync(); - try { - assertThat(updateCount.get()).isEqualTo(UPDATE_COUNT); - assertThat(commitTimestamp.get()).isNotNull(); - break; - } catch (AbortedException e) { - txn = txMgr.resetForRetryAsync(); - } - } - } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - - @Test - public void testAsyncTransactionManagerInlinedBeginTxAborted() - throws InterruptedException, ExecutionException { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - try (AsyncTransactionManager txMgr = client.transactionManagerAsync()) { - TransactionContextFuture txn = txMgr.beginAsync(); - boolean first = true; - while (true) { - try { - AsyncTransactionStep updateCount = - txn.then( - new AsyncTransactionFunction() { - @Override - public ApiFuture apply(TransactionContext txn, Void input) - throws Exception { - return txn.executeUpdateAsync(UPDATE_STATEMENT); + }, + MoreExecutors.directExecutor()); } }, executor); - if (first) { - // Abort the transaction after the statement has been executed to ensure that the - // transaction has actually been started before the test tries to abort it. - updateCount.then( - new AsyncTransactionFunction() { - @Override - public ApiFuture apply(TransactionContext txn, Long input) - throws Exception { - mockSpanner.abortAllTransactions(); - return ApiFutures.immediateFuture(null); - } - }, - MoreExecutors.directExecutor()); - first = false; - } - assertThat(updateCount.commitAsync().get()).isNotNull(); - assertThat(updateCount.get()).isEqualTo(UPDATE_COUNT); - break; - } catch (AbortedException e) { - txn = txMgr.resetForRetryAsync(); - } - } + assertThat(updateCount.get()).isEqualTo(1L * numQueries); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countTransactionsStarted()).isEqualTo(1); } - // The retry will use a BeginTransaction RPC. - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); - assertThat(countTransactionsStarted()).isEqualTo(2); - } - @Test - public void testAsyncTransactionManagerInlinedBeginTxWithOnlyMutations() - throws InterruptedException, ExecutionException { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - try (AsyncTransactionManager txMgr = client.transactionManagerAsync()) { - TransactionContextFuture txn = txMgr.beginAsync(); - while (true) { - try { - txn.then( - new AsyncTransactionFunction() { - @Override - public ApiFuture apply(TransactionContext txn, Void input) - throws Exception { - txn.buffer(Mutation.newInsertBuilder("FOO").set("ID").to(1L).build()); - return ApiFutures.immediateFuture(null); - } - }, - executor) - .commitAsync() - .get(); - break; - } catch (AbortedException e) { - txn = txMgr.resetForRetryAsync(); - } - } + @Test + public void queryWithoutNext() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + assertThat( + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Long run(TransactionContext transaction) throws Exception { + // This will not actually send an RPC, so it will also not request a + // transaction. + transaction.executeQuery(SELECT1); + return transaction.executeUpdate(UPDATE_STATEMENT); + } + })) + .isEqualTo(UPDATE_COUNT); + assertThat(mockSpanner.countRequestsOfType(BeginTransactionRequest.class)).isEqualTo(0L); + assertThat(mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)).isEqualTo(1L); + assertThat(countTransactionsStarted()).isEqualTo(1); } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - @Test - public void testAsyncTransactionManagerInlinedBeginTxWithError() - throws InterruptedException, ExecutionException { - DatabaseClient client = - spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); - try (AsyncTransactionManager txMgr = client.transactionManagerAsync()) { - TransactionContextFuture txn = txMgr.beginAsync(); - while (true) { - try { - AsyncTransactionStep updateCount = - txn.then( - new AsyncTransactionFunction() { + @Test + public void queryAsyncWithoutCallback() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + assertThat( + client + .readWriteTransaction() + .run( + new TransactionCallable() { @Override - public ApiFuture apply(TransactionContext txn, Void input) - throws Exception { - return txn.executeUpdateAsync(INVALID_UPDATE_STATEMENT); + public Long run(TransactionContext transaction) throws Exception { + transaction.executeQueryAsync(SELECT1); + return transaction.executeUpdate(UPDATE_STATEMENT); } - }, - executor) - .then( - new AsyncTransactionFunction() { + })) + .isEqualTo(UPDATE_COUNT); + assertThat(mockSpanner.countRequestsOfType(BeginTransactionRequest.class)).isEqualTo(0L); + assertThat(mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)).isEqualTo(1L); + assertThat(countTransactionsStarted()).isEqualTo(1); + } + + @Test + public void readWithoutNext() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + assertThat( + client + .readWriteTransaction() + .run( + new TransactionCallable() { @Override - public ApiFuture apply(TransactionContext txn, Long input) - throws Exception { - return txn.executeUpdateAsync(UPDATE_STATEMENT); + public Long run(TransactionContext transaction) throws Exception { + transaction.read("FOO", KeySet.all(), Arrays.asList("ID")); + return transaction.executeUpdate(UPDATE_STATEMENT); } - }, - executor); - try { - updateCount.commitAsync().get(); - fail("missing expected exception"); - } catch (ExecutionException e) { - assertThat(e.getCause()).isInstanceOf(SpannerException.class); - SpannerException se = (SpannerException) e.getCause(); - assertThat(se.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); - } - break; - } catch (AbortedException e) { - txn = txMgr.resetForRetryAsync(); - } - } + })) + .isEqualTo(UPDATE_COUNT); + assertThat(mockSpanner.countRequestsOfType(BeginTransactionRequest.class)).isEqualTo(0L); + assertThat(mockSpanner.countRequestsOfType(ReadRequest.class)).isEqualTo(0L); + assertThat(mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)).isEqualTo(1L); + assertThat(countTransactionsStarted()).isEqualTo(1); } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - - @Test - public void queryWithoutNext() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - assertThat( - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(TransactionContext transaction) throws Exception { - // This will not actually send an RPC, so it will also not request a - // transaction. - transaction.executeQuery(SELECT1); - return transaction.executeUpdate(UPDATE_STATEMENT); - } - })) - .isEqualTo(UPDATE_COUNT); - assertThat(mockSpanner.countRequestsOfType(BeginTransactionRequest.class)).isEqualTo(0L); - assertThat(mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)).isEqualTo(1L); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - - @Test - public void queryAsyncWithoutCallback() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - assertThat( - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(TransactionContext transaction) throws Exception { - transaction.executeQueryAsync(SELECT1); - return transaction.executeUpdate(UPDATE_STATEMENT); - } - })) - .isEqualTo(UPDATE_COUNT); - assertThat(mockSpanner.countRequestsOfType(BeginTransactionRequest.class)).isEqualTo(0L); - assertThat(mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)).isEqualTo(1L); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - - @Test - public void readWithoutNext() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - assertThat( - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(TransactionContext transaction) throws Exception { - transaction.read("FOO", KeySet.all(), Arrays.asList("ID")); - return transaction.executeUpdate(UPDATE_STATEMENT); - } - })) - .isEqualTo(UPDATE_COUNT); - assertThat(mockSpanner.countRequestsOfType(BeginTransactionRequest.class)).isEqualTo(0L); - assertThat(mockSpanner.countRequestsOfType(ReadRequest.class)).isEqualTo(0L); - assertThat(mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)).isEqualTo(1L); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - - @Test - public void readAsyncWithoutCallback() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - assertThat( - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(TransactionContext transaction) throws Exception { - transaction.readAsync("FOO", KeySet.all(), Arrays.asList("ID")); - return transaction.executeUpdate(UPDATE_STATEMENT); - } - })) - .isEqualTo(UPDATE_COUNT); - assertThat(mockSpanner.countRequestsOfType(BeginTransactionRequest.class)).isEqualTo(0L); - assertThat(mockSpanner.countRequestsOfType(ReadRequest.class)).isEqualTo(0L); - assertThat(mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)).isEqualTo(1L); - assertThat(countTransactionsStarted()).isEqualTo(1); - } - - @Test - public void query_ThenUpdate_ThenConsumeResultSet() - throws InterruptedException, TimeoutException { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - assertThat( - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(TransactionContext transaction) throws Exception { - ResultSet rs = transaction.executeQuery(SELECT1); - long updateCount = transaction.executeUpdate(UPDATE_STATEMENT); - // Consume the result set. - while (rs.next()) {} - return updateCount; - } - })) - .isEqualTo(UPDATE_COUNT); - // The update statement should start the transaction, and the query should use the transaction - // id returned by the update. - assertThat(mockSpanner.countRequestsOfType(BeginTransactionRequest.class)).isEqualTo(0L); - assertThat(mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)).isEqualTo(2L); - assertThat(mockSpanner.countRequestsOfType(CommitRequest.class)).isEqualTo(1L); - assertThat(countTransactionsStarted()).isEqualTo(1); - List requests = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class); - assertThat(requests.get(0)).isInstanceOf(ExecuteSqlRequest.class); - assertThat(((ExecuteSqlRequest) requests.get(0)).getSql()).isEqualTo(UPDATE_STATEMENT.getSql()); - assertThat(requests.get(1)).isInstanceOf(ExecuteSqlRequest.class); - assertThat(((ExecuteSqlRequest) requests.get(1)).getSql()).isEqualTo(SELECT1.getSql()); - } - - @Test - public void testInlinedBeginTxWithStreamRetry() { - mockSpanner.setExecuteStreamingSqlExecutionTime( - SimulatedExecutionTime.ofStreamException(Status.UNAVAILABLE.asRuntimeException(), 1)); - - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Void run(TransactionContext transaction) throws Exception { - try (ResultSet rs = transaction.executeQuery(SELECT1_UNION_ALL_SELECT2)) { - while (rs.next()) {} - } - return null; - } - }); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(2); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - assertThat(countTransactionsStarted()).isEqualTo(1); - - List requests = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class); - assertThat(requests.get(0)).isInstanceOf(ExecuteSqlRequest.class); - ExecuteSqlRequest request1 = (ExecuteSqlRequest) requests.get(0); - assertThat(request1.getSql()).isEqualTo(SELECT1_UNION_ALL_SELECT2.getSql()); - assertThat(request1.getTransaction().getBegin().hasReadWrite()).isTrue(); - assertThat(request1.getTransaction().getId()).isEqualTo(ByteString.EMPTY); - assertThat(request1.getResumeToken()).isEqualTo(ByteString.EMPTY); - - assertThat(requests.get(1)).isInstanceOf(ExecuteSqlRequest.class); - ExecuteSqlRequest request2 = (ExecuteSqlRequest) requests.get(1); - assertThat(request2.getSql()).isEqualTo(SELECT1_UNION_ALL_SELECT2.getSql()); - assertThat(request2.getTransaction().hasBegin()).isFalse(); - assertThat(request2.getTransaction().getId()).isNotEqualTo(ByteString.EMPTY); - assertThat(request2.getResumeToken()).isNotEqualTo(ByteString.EMPTY); - } - @Test - public void testWaitForTransactionTimeout() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - client - .readWriteTransaction() - .run( - new TransactionCallable() { - int attempt = 0; - - @Override - public Void run(TransactionContext transaction) throws Exception { - attempt++; - TransactionContextImpl impl = (TransactionContextImpl) transaction; - if (attempt == 1) { - impl.waitForTransactionTimeoutMillis = 1L; - // Freeze the mock server to prevent the first (async) statement from returning a - // transaction. - mockSpanner.freeze(); - } else { - impl.waitForTransactionTimeoutMillis = 60_000L; - } - transaction.executeUpdateAsync(UPDATE_STATEMENT); - - // Try to execute a query. This will timeout during the first attempt while waiting - // for the first statement to return a transaction, and then force a retry of the - // transaction. - try (ResultSet rs = transaction.executeQuery(SELECT1)) { - while (rs.next()) {} - } catch (Throwable t) { - mockSpanner.unfreeze(); - throw t; - } - return null; - } - }); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(3); - assertThat(countRequests(CommitRequest.class)).isEqualTo(1); - } - - @Test - public void testCloseResultSetWhileRequestInFlight() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - final ExecutorService service = Executors.newSingleThreadExecutor(); - try { - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Void run(TransactionContext transaction) throws Exception { - final ResultSet rs = transaction.executeQuery(SELECT1); - // Prevent the server from executing the query. - mockSpanner.freeze(); - service.submit( - new Runnable() { + @Test + public void readAsyncWithoutCallback() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + assertThat( + client + .readWriteTransaction() + .run( + new TransactionCallable() { @Override - public void run() { - // This call will be stuck on the server until the mock server is - // unfrozen. - rs.next(); + public Long run(TransactionContext transaction) throws Exception { + transaction.readAsync("FOO", KeySet.all(), Arrays.asList("ID")); + return transaction.executeUpdate(UPDATE_STATEMENT); } - }); + })) + .isEqualTo(UPDATE_COUNT); + assertThat(mockSpanner.countRequestsOfType(BeginTransactionRequest.class)).isEqualTo(0L); + assertThat(mockSpanner.countRequestsOfType(ReadRequest.class)).isEqualTo(0L); + assertThat(mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)).isEqualTo(1L); + assertThat(countTransactionsStarted()).isEqualTo(1); + } - // Close the result set while the request is in flight. - mockSpanner.waitForRequestsToContain( - new Predicate() { + @Test + public void query_ThenUpdate_ThenConsumeResultSet() + throws InterruptedException, TimeoutException { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + assertThat( + client + .readWriteTransaction() + .run( + new TransactionCallable() { @Override - public boolean apply(AbstractMessage input) { - return input instanceof ExecuteSqlRequest - && ((ExecuteSqlRequest) input).getTransaction().hasBegin(); + public Long run(TransactionContext transaction) throws Exception { + ResultSet rs = transaction.executeQuery(SELECT1); + long updateCount = transaction.executeUpdate(UPDATE_STATEMENT); + // Consume the result set. + while (rs.next()) {} + return updateCount; } - }, - 100L); - rs.close(); - // The next statement should now fail before it is sent to the server because the - // first statement failed to return a transaction while the result set was still - // open. - mockSpanner.unfreeze(); - try { - transaction.executeUpdate(UPDATE_STATEMENT); - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - assertThat(e.getMessage()) - .contains("ResultSet was closed before a transaction id was returned"); - } - return null; - } - }); - fail("missing expected exception"); - } catch (SpannerException e) { - // The commit request will also fail, which means that the entire transaction will fail. - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - assertThat(e.getMessage()) - .contains("ResultSet was closed before a transaction id was returned"); + })) + .isEqualTo(UPDATE_COUNT); + // The update statement should start the transaction, and the query should use the transaction + // id returned by the update. + assertThat(mockSpanner.countRequestsOfType(BeginTransactionRequest.class)).isEqualTo(0L); + assertThat(mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)).isEqualTo(2L); + assertThat(mockSpanner.countRequestsOfType(CommitRequest.class)).isEqualTo(1L); + assertThat(countTransactionsStarted()).isEqualTo(1); + List requests = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class); + assertThat(requests.get(0)).isInstanceOf(ExecuteSqlRequest.class); + assertThat(((ExecuteSqlRequest) requests.get(0)).getSql()) + .isEqualTo(UPDATE_STATEMENT.getSql()); + assertThat(requests.get(1)).isInstanceOf(ExecuteSqlRequest.class); + assertThat(((ExecuteSqlRequest) requests.get(1)).getSql()).isEqualTo(SELECT1.getSql()); } - service.shutdown(); - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(0); - } - @Test - public void testQueryWithInlineBeginDidNotReturnTransaction() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - // This will cause the first statement that requests a transaction to not return a transaction - // id. - mockSpanner.ignoreNextInlineBeginRequest(); - try { + @Test + public void testInlinedBeginTxWithStreamRetry() { + mockSpanner.setExecuteStreamingSqlExecutionTime( + SimulatedExecutionTime.ofStreamException(Status.UNAVAILABLE.asRuntimeException(), 1)); + + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); client .readWriteTransaction() .run( @@ -1564,202 +1436,355 @@ public Void run(TransactionContext transaction) throws Exception { return null; } }); - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - assertThat(e.getMessage()).contains(AbstractReadContext.NO_TRANSACTION_RETURNED_MSG); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(2); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); + assertThat(countTransactionsStarted()).isEqualTo(1); + + List requests = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class); + assertThat(requests.get(0)).isInstanceOf(ExecuteSqlRequest.class); + ExecuteSqlRequest request1 = (ExecuteSqlRequest) requests.get(0); + assertThat(request1.getSql()).isEqualTo(SELECT1_UNION_ALL_SELECT2.getSql()); + assertThat(request1.getTransaction().getBegin().hasReadWrite()).isTrue(); + assertThat(request1.getTransaction().getId()).isEqualTo(ByteString.EMPTY); + assertThat(request1.getResumeToken()).isEqualTo(ByteString.EMPTY); + + assertThat(requests.get(1)).isInstanceOf(ExecuteSqlRequest.class); + ExecuteSqlRequest request2 = (ExecuteSqlRequest) requests.get(1); + assertThat(request2.getSql()).isEqualTo(SELECT1_UNION_ALL_SELECT2.getSql()); + assertThat(request2.getTransaction().hasBegin()).isFalse(); + assertThat(request2.getTransaction().getId()).isNotEqualTo(ByteString.EMPTY); + assertThat(request2.getResumeToken()).isNotEqualTo(ByteString.EMPTY); } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(0); - } - @Test - public void testReadWithInlineBeginDidNotReturnTransaction() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - // This will cause the first statement that requests a transaction to not return a transaction - // id. - mockSpanner.ignoreNextInlineBeginRequest(); - try { + @Test + public void testWaitForTransactionTimeout() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); client .readWriteTransaction() .run( new TransactionCallable() { + int attempt = 0; + @Override public Void run(TransactionContext transaction) throws Exception { - transaction.readRow("FOO", Key.of(1L), Arrays.asList("BAR")); + attempt++; + TransactionContextImpl impl = (TransactionContextImpl) transaction; + if (attempt == 1) { + impl.waitForTransactionTimeoutMillis = 1L; + // Freeze the mock server to prevent the first (async) statement from returning + // a + // transaction. + mockSpanner.freeze(); + } else { + impl.waitForTransactionTimeoutMillis = 60_000L; + } + transaction.executeUpdateAsync(UPDATE_STATEMENT); + + // Try to execute a query. This will timeout during the first attempt while + // waiting + // for the first statement to return a transaction, and then force a retry of the + // transaction. + try (ResultSet rs = transaction.executeQuery(SELECT1)) { + while (rs.next()) {} + } catch (Throwable t) { + mockSpanner.unfreeze(); + throw t; + } return null; } }); - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - assertThat(e.getMessage()).contains(AbstractReadContext.NO_TRANSACTION_RETURNED_MSG); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(3); + assertThat(countRequests(CommitRequest.class)).isEqualTo(1); } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ReadRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(0); - } - @Test - public void testUpdateWithInlineBeginDidNotReturnTransaction() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - // This will cause the first statement that requests a transaction to not return a transaction - // id. - mockSpanner.ignoreNextInlineBeginRequest(); - try { - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Void run(TransactionContext transaction) throws Exception { - transaction.executeUpdate(UPDATE_STATEMENT); - return null; - } - }); - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - assertThat(e.getMessage()).contains(AbstractReadContext.NO_TRANSACTION_RETURNED_MSG); + @Test + public void testCloseResultSetWhileRequestInFlight() throws Exception { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + final ExecutorService service = Executors.newSingleThreadExecutor(); + try { + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + final ResultSet rs = transaction.executeQuery(SELECT1); + // Prevent the server from executing the query. + final CountDownLatch latch = new CountDownLatch(1); + mockSpanner.freeze(); + service.submit( + new Runnable() { + @Override + public void run() { + try { + // This call will be stuck on the server until the mock server is + // unfrozen. + rs.next(); + } finally { + latch.countDown(); + } + } + }); + + // First wait for the request to be on the server and then close the result set + // while the request is in flight. + mockSpanner.waitForRequestsToContain( + new Predicate() { + @Override + public boolean apply(AbstractMessage input) { + return input instanceof ExecuteSqlRequest + && ((ExecuteSqlRequest) input).getTransaction().hasBegin(); + } + }, + 1000L); + rs.close(); + // The next statement should now fail before it is sent to the server because + // the first statement failed to return a transaction while the result set was + // still open. + mockSpanner.unfreeze(); + latch.await(1L, TimeUnit.SECONDS); + try { + transaction.executeUpdate(UPDATE_STATEMENT); + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + assertThat(e.getMessage()) + .contains("ResultSet was closed before a transaction id was returned"); + } + return null; + } + }); + fail("missing expected exception"); + } catch (SpannerException e) { + // The commit request will also fail, which means that the entire transaction will fail. + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + assertThat(e.getMessage()) + .contains("ResultSet was closed before a transaction id was returned"); + } + service.shutdown(); + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(0); } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(0); - } - @Test - public void testBatchUpdateWithInlineBeginDidNotReturnTransaction() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - // This will cause the first statement that requests a transaction to not return a transaction - // id. - mockSpanner.ignoreNextInlineBeginRequest(); - try { - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Void run(TransactionContext transaction) throws Exception { - transaction.batchUpdate(Arrays.asList(UPDATE_STATEMENT)); - return null; - } - }); - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - assertThat(e.getMessage()).contains(AbstractReadContext.NO_TRANSACTION_RETURNED_MSG); + @Test + public void testQueryWithInlineBeginDidNotReturnTransaction() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + // This will cause the first statement that requests a transaction to not return a transaction + // id. + mockSpanner.ignoreNextInlineBeginRequest(); + try { + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + try (ResultSet rs = transaction.executeQuery(SELECT1_UNION_ALL_SELECT2)) { + while (rs.next()) {} + } + return null; + } + }); + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + assertThat(e.getMessage()).contains(AbstractReadContext.NO_TRANSACTION_RETURNED_MSG); + } + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(0); } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteBatchDmlRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(0); - } - @Test - public void testQueryAsyncWithInlineBeginDidNotReturnTransaction() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - final ExecutorService executor = Executors.newSingleThreadExecutor(); - // This will cause the first statement that requests a transaction to not return a transaction - // id. - mockSpanner.ignoreNextInlineBeginRequest(); - try { - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Void run(TransactionContext transaction) throws Exception { - try (AsyncResultSet rs = - transaction.executeQueryAsync(SELECT1_UNION_ALL_SELECT2)) { - return SpannerApiFutures.get( - rs.setCallback( - executor, - new ReadyCallback() { - @Override - public CallbackResponse cursorReady(AsyncResultSet resultSet) { - try { - while (true) { - switch (resultSet.tryNext()) { - case OK: - break; - case DONE: - return CallbackResponse.DONE; - case NOT_READY: - return CallbackResponse.CONTINUE; + @Test + public void testReadWithInlineBeginDidNotReturnTransaction() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + // This will cause the first statement that requests a transaction to not return a transaction + // id. + mockSpanner.ignoreNextInlineBeginRequest(); + try { + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + transaction.readRow("FOO", Key.of(1L), Arrays.asList("BAR")); + return null; + } + }); + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + assertThat(e.getMessage()).contains(AbstractReadContext.NO_TRANSACTION_RETURNED_MSG); + } + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ReadRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(0); + } + + @Test + public void testUpdateWithInlineBeginDidNotReturnTransaction() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + // This will cause the first statement that requests a transaction to not return a transaction + // id. + mockSpanner.ignoreNextInlineBeginRequest(); + try { + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + transaction.executeUpdate(UPDATE_STATEMENT); + return null; + } + }); + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + assertThat(e.getMessage()).contains(AbstractReadContext.NO_TRANSACTION_RETURNED_MSG); + } + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(0); + } + + @Test + public void testBatchUpdateWithInlineBeginDidNotReturnTransaction() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + // This will cause the first statement that requests a transaction to not return a transaction + // id. + mockSpanner.ignoreNextInlineBeginRequest(); + try { + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + transaction.batchUpdate(Arrays.asList(UPDATE_STATEMENT)); + return null; + } + }); + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + assertThat(e.getMessage()).contains(AbstractReadContext.NO_TRANSACTION_RETURNED_MSG); + } + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteBatchDmlRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(0); + } + + @Test + public void testQueryAsyncWithInlineBeginDidNotReturnTransaction() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + final ExecutorService executor = Executors.newSingleThreadExecutor(); + // This will cause the first statement that requests a transaction to not return a transaction + // id. + mockSpanner.ignoreNextInlineBeginRequest(); + try { + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + try (AsyncResultSet rs = + transaction.executeQueryAsync(SELECT1_UNION_ALL_SELECT2)) { + return SpannerApiFutures.get( + rs.setCallback( + executor, + new ReadyCallback() { + @Override + public CallbackResponse cursorReady(AsyncResultSet resultSet) { + try { + while (true) { + switch (resultSet.tryNext()) { + case OK: + break; + case DONE: + return CallbackResponse.DONE; + case NOT_READY: + return CallbackResponse.CONTINUE; + } } + } catch (SpannerException e) { + return CallbackResponse.DONE; } - } catch (SpannerException e) { - return CallbackResponse.DONE; } - } - })); + })); + } } - } - }); - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - assertThat(e.getMessage()).contains(AbstractReadContext.NO_TRANSACTION_RETURNED_MSG); + }); + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + assertThat(e.getMessage()).contains(AbstractReadContext.NO_TRANSACTION_RETURNED_MSG); + } + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(0); } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(0); - } - @Test - public void testUpdateAsyncWithInlineBeginDidNotReturnTransaction() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - // This will cause the first statement that requests a transaction to not return a transaction - // id. - mockSpanner.ignoreNextInlineBeginRequest(); - try { - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public Long run(TransactionContext transaction) throws Exception { - return SpannerApiFutures.get(transaction.executeUpdateAsync(UPDATE_STATEMENT)); - } - }); - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - assertThat(e.getMessage()).contains(AbstractReadContext.NO_TRANSACTION_RETURNED_MSG); + @Test + public void testUpdateAsyncWithInlineBeginDidNotReturnTransaction() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + // This will cause the first statement that requests a transaction to not return a transaction + // id. + mockSpanner.ignoreNextInlineBeginRequest(); + try { + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public Long run(TransactionContext transaction) throws Exception { + return SpannerApiFutures.get(transaction.executeUpdateAsync(UPDATE_STATEMENT)); + } + }); + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + assertThat(e.getMessage()).contains(AbstractReadContext.NO_TRANSACTION_RETURNED_MSG); + } + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(0); } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteSqlRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(0); - } - @Test - public void testBatchUpdateAsyncWithInlineBeginDidNotReturnTransaction() { - DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); - // This will cause the first statement that requests a transaction to not return a transaction - // id. - mockSpanner.ignoreNextInlineBeginRequest(); - try { - client - .readWriteTransaction() - .run( - new TransactionCallable() { - @Override - public long[] run(TransactionContext transaction) throws Exception { - return SpannerApiFutures.get( - transaction.batchUpdateAsync(Arrays.asList(UPDATE_STATEMENT))); - } - }); - fail("missing expected exception"); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - assertThat(e.getMessage()).contains(AbstractReadContext.NO_TRANSACTION_RETURNED_MSG); + @Test + public void testBatchUpdateAsyncWithInlineBeginDidNotReturnTransaction() { + DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + // This will cause the first statement that requests a transaction to not return a transaction + // id. + mockSpanner.ignoreNextInlineBeginRequest(); + try { + client + .readWriteTransaction() + .run( + new TransactionCallable() { + @Override + public long[] run(TransactionContext transaction) throws Exception { + return SpannerApiFutures.get( + transaction.batchUpdateAsync(Arrays.asList(UPDATE_STATEMENT))); + } + }); + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + assertThat(e.getMessage()).contains(AbstractReadContext.NO_TRANSACTION_RETURNED_MSG); + } + assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); + assertThat(countRequests(ExecuteBatchDmlRequest.class)).isEqualTo(1); + assertThat(countRequests(CommitRequest.class)).isEqualTo(0); } - assertThat(countRequests(BeginTransactionRequest.class)).isEqualTo(0); - assertThat(countRequests(ExecuteBatchDmlRequest.class)).isEqualTo(1); - assertThat(countRequests(CommitRequest.class)).isEqualTo(0); } - private int countRequests(Class requestType) { + private static int countRequests(Class requestType) { int count = 0; for (AbstractMessage msg : mockSpanner.getRequests()) { if (msg.getClass().equals(requestType)) { @@ -1769,7 +1794,7 @@ private int countRequests(Class requestType) { return count; } - private int countTransactionsStarted() { + private static int countTransactionsStarted() { return mockSpanner.getTransactionsStarted().size(); } } 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 5dc442a62e3..17714ebae0c 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 @@ -1966,7 +1966,7 @@ public void waitForLastRequestToBe(Class type, long t Stopwatch watch = Stopwatch.createStarted(); while (!(this.requests.peekLast() != null && this.requests.peekLast().getClass().equals(type))) { - Thread.sleep(10L); + Thread.sleep(1L); if (watch.elapsed(TimeUnit.MILLISECONDS) > timeoutMillis) { throw new TimeoutException( "Timeout while waiting for last request to become " + type.getName()); @@ -1982,7 +1982,7 @@ public void waitForRequestsToContain(Class type, long throws InterruptedException, TimeoutException { Stopwatch watch = Stopwatch.createStarted(); while (countRequestsOfType(type) == 0) { - Thread.sleep(10L); + Thread.sleep(1L); if (watch.elapsed(TimeUnit.MILLISECONDS) > timeoutMillis) { throw new TimeoutException( "Timeout while waiting for requests to contain " + type.getName()); @@ -1999,7 +1999,7 @@ public void waitForRequestsToContain( if (msg.iterator().hasNext()) { break; } - Thread.sleep(10L); + Thread.sleep(1L); if (watch.elapsed(TimeUnit.MILLISECONDS) > timeoutMillis) { throw new TimeoutException( "Timeout while waiting for requests to contain the wanted request"); From 49c8c5d241803565fa9ff96ba55f3eb00ed5b85e Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Mon, 1 Feb 2021 01:02:42 +0100 Subject: [PATCH 16/42] deps: update dependency com.google.cloud:google-cloud-trace to v1.2.11 (#825) --- samples/install-without-bom/pom.xml | 2 +- samples/snapshot/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index 9be81c3b67c..8658f8208b0 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -22,7 +22,7 @@ 1.8 UTF-8 0.28.3 - 1.2.10 + 1.2.11 2.0.11 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index bec35d771e8..93f5eff505b 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -22,7 +22,7 @@ 1.8 UTF-8 0.28.3 - 1.2.10 + 1.2.11 2.0.11 From 717700283e79c640cf1184e256e14d657fb0ffc7 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Sun, 31 Jan 2021 16:38:23 -0800 Subject: [PATCH 17/42] chore: updates flakybot to new name buildcop (#828) --- .kokoro/build.sh | 4 ++-- synth.metadata | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.kokoro/build.sh b/.kokoro/build.sh index 4572da72d57..8280d10ad67 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -110,8 +110,8 @@ bash .kokoro/coerce_logs.sh if [[ "${ENABLE_BUILD_COP}" == "true" ]] then - chmod +x ${KOKORO_GFILE_DIR}/linux_amd64/flakybot - ${KOKORO_GFILE_DIR}/linux_amd64/flakybot -repo=googleapis/java-spanner + chmod +x ${KOKORO_GFILE_DIR}/linux_amd64/buildcop + ${KOKORO_GFILE_DIR}/linux_amd64/buildcop -repo=googleapis/java-spanner fi echo "exiting with ${RETURN_CODE}" diff --git a/synth.metadata b/synth.metadata index 0767da9c0e4..d9eb2060851 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "2f1450cb4780590d6660ebae56f96331b78dd973" + "sha": "facda8a7d07a9cf08b1847678b24b270f1abdb38" } }, { @@ -60,7 +60,6 @@ ".github/blunderbuss.yml", ".github/generated-files-bot.yml", ".github/readme/synth.py", - ".github/release-please.yml", ".github/snippet-bot.yml", ".github/trusted-contribution.yml", ".github/workflows/approve-readme.yaml", From 004c5d759804a380e65d1023faef0978de5d3ff9 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Sun, 31 Jan 2021 16:42:00 -0800 Subject: [PATCH 18/42] chore: adds PITR fields to backup and database (#829) * chore: updates flakybot to new name buildcop * feat: adds PITR fields to backup and database PiperOrigin-RevId: 354462069 Source-Author: Google APIs Source-Date: Thu Jan 28 21:22:16 2021 -0800 Source-Repo: googleapis/googleapis Source-Sha: 30eb779ddcc4f389df34d0802d081ef6c4559d6d Source-Link: https://github.com/googleapis/googleapis/commit/30eb779ddcc4f389df34d0802d081ef6c4559d6d --- .../database/v1/DatabaseAdminClient.java | 6 +- .../database/v1/DatabaseAdminClientTest.java | 21 + .../spanner/admin/database/v1/Backup.java | 394 +++++++++++-- .../spanner/admin/database/v1/BackupInfo.java | 398 +++++++++++-- .../database/v1/BackupInfoOrBuilder.java | 65 ++- .../admin/database/v1/BackupOrBuilder.java | 65 ++- .../admin/database/v1/BackupProto.java | 107 ++-- .../database/v1/CreateBackupMetadata.java | 28 +- .../v1/CreateBackupMetadataOrBuilder.java | 8 +- .../spanner/admin/database/v1/Database.java | 524 ++++++++++++++++++ .../admin/database/v1/DatabaseOrBuilder.java | 77 +++ .../database/v1/GetDatabaseDdlRequest.java | 14 + .../v1/GetDatabaseDdlRequestOrBuilder.java | 4 + .../v1/ListBackupOperationsRequest.java | 42 +- .../ListBackupOperationsRequestOrBuilder.java | 12 +- .../v1/ListDatabaseOperationsRequest.java | 70 +-- ...istDatabaseOperationsRequestOrBuilder.java | 20 +- .../v1/OptimizeRestoredDatabaseMetadata.java | 14 +- ...mizeRestoredDatabaseMetadataOrBuilder.java | 4 +- .../database/v1/RestoreDatabaseMetadata.java | 14 +- .../v1/RestoreDatabaseMetadataOrBuilder.java | 4 +- .../v1/SpannerDatabaseAdminProto.java | 344 ++++++------ .../spanner/admin/database/v1/backup.proto | 44 +- .../database/v1/spanner_database_admin.proto | 31 +- synth.metadata | 4 +- 25 files changed, 1859 insertions(+), 455 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClient.java index c30e4ab1269..cab26e07702 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClient.java @@ -570,7 +570,8 @@ public final UnaryCallable dropDatabaseCallable() { * method does not show pending schema updates, those may be queried using the * [Operations][google.longrunning.Operations] API. * - * @param database Required. The database whose schema we wish to get. + * @param database Required. The database whose schema we wish to get. Values are of the form + * `projects/<project>/instances/<instance>/databases/<database>` * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ public final GetDatabaseDdlResponse getDatabaseDdl(DatabaseName database) { @@ -587,7 +588,8 @@ public final GetDatabaseDdlResponse getDatabaseDdl(DatabaseName database) { * method does not show pending schema updates, those may be queried using the * [Operations][google.longrunning.Operations] API. * - * @param database Required. The database whose schema we wish to get. + * @param database Required. The database whose schema we wish to get. Values are of the form + * `projects/<project>/instances/<instance>/databases/<database>` * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ public final GetDatabaseDdlResponse getDatabaseDdl(String database) { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClientTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClientTest.java index 956f66ee6cf..20a1869946e 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClientTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClientTest.java @@ -217,6 +217,8 @@ public void createDatabaseTest() throws Exception { .setName(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString()) .setCreateTime(Timestamp.newBuilder().build()) .setRestoreInfo(RestoreInfo.newBuilder().build()) + .setVersionRetentionPeriod("versionRetentionPeriod-629783929") + .setEarliestVersionTime(Timestamp.newBuilder().build()) .build(); Operation resultOperation = Operation.newBuilder() @@ -268,6 +270,8 @@ public void createDatabaseTest2() throws Exception { .setName(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString()) .setCreateTime(Timestamp.newBuilder().build()) .setRestoreInfo(RestoreInfo.newBuilder().build()) + .setVersionRetentionPeriod("versionRetentionPeriod-629783929") + .setEarliestVersionTime(Timestamp.newBuilder().build()) .build(); Operation resultOperation = Operation.newBuilder() @@ -319,6 +323,8 @@ public void getDatabaseTest() throws Exception { .setName(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString()) .setCreateTime(Timestamp.newBuilder().build()) .setRestoreInfo(RestoreInfo.newBuilder().build()) + .setVersionRetentionPeriod("versionRetentionPeriod-629783929") + .setEarliestVersionTime(Timestamp.newBuilder().build()) .build(); mockDatabaseAdmin.addResponse(expectedResponse); @@ -359,6 +365,8 @@ public void getDatabaseTest2() throws Exception { .setName(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString()) .setCreateTime(Timestamp.newBuilder().build()) .setRestoreInfo(RestoreInfo.newBuilder().build()) + .setVersionRetentionPeriod("versionRetentionPeriod-629783929") + .setEarliestVersionTime(Timestamp.newBuilder().build()) .build(); mockDatabaseAdmin.addResponse(expectedResponse); @@ -871,6 +879,7 @@ public void createBackupTest() throws Exception { Backup expectedResponse = Backup.newBuilder() .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString()) + .setVersionTime(Timestamp.newBuilder().build()) .setExpireTime(Timestamp.newBuilder().build()) .setName(BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]").toString()) .setCreateTime(Timestamp.newBuilder().build()) @@ -928,6 +937,7 @@ public void createBackupTest2() throws Exception { Backup expectedResponse = Backup.newBuilder() .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString()) + .setVersionTime(Timestamp.newBuilder().build()) .setExpireTime(Timestamp.newBuilder().build()) .setName(BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]").toString()) .setCreateTime(Timestamp.newBuilder().build()) @@ -985,6 +995,7 @@ public void getBackupTest() throws Exception { Backup expectedResponse = Backup.newBuilder() .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString()) + .setVersionTime(Timestamp.newBuilder().build()) .setExpireTime(Timestamp.newBuilder().build()) .setName(BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]").toString()) .setCreateTime(Timestamp.newBuilder().build()) @@ -1028,6 +1039,7 @@ public void getBackupTest2() throws Exception { Backup expectedResponse = Backup.newBuilder() .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString()) + .setVersionTime(Timestamp.newBuilder().build()) .setExpireTime(Timestamp.newBuilder().build()) .setName(BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]").toString()) .setCreateTime(Timestamp.newBuilder().build()) @@ -1071,6 +1083,7 @@ public void updateBackupTest() throws Exception { Backup expectedResponse = Backup.newBuilder() .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString()) + .setVersionTime(Timestamp.newBuilder().build()) .setExpireTime(Timestamp.newBuilder().build()) .setName(BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]").toString()) .setCreateTime(Timestamp.newBuilder().build()) @@ -1275,6 +1288,8 @@ public void restoreDatabaseTest() throws Exception { .setName(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString()) .setCreateTime(Timestamp.newBuilder().build()) .setRestoreInfo(RestoreInfo.newBuilder().build()) + .setVersionRetentionPeriod("versionRetentionPeriod-629783929") + .setEarliestVersionTime(Timestamp.newBuilder().build()) .build(); Operation resultOperation = Operation.newBuilder() @@ -1329,6 +1344,8 @@ public void restoreDatabaseTest2() throws Exception { .setName(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString()) .setCreateTime(Timestamp.newBuilder().build()) .setRestoreInfo(RestoreInfo.newBuilder().build()) + .setVersionRetentionPeriod("versionRetentionPeriod-629783929") + .setEarliestVersionTime(Timestamp.newBuilder().build()) .build(); Operation resultOperation = Operation.newBuilder() @@ -1383,6 +1400,8 @@ public void restoreDatabaseTest3() throws Exception { .setName(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString()) .setCreateTime(Timestamp.newBuilder().build()) .setRestoreInfo(RestoreInfo.newBuilder().build()) + .setVersionRetentionPeriod("versionRetentionPeriod-629783929") + .setEarliestVersionTime(Timestamp.newBuilder().build()) .build(); Operation resultOperation = Operation.newBuilder() @@ -1437,6 +1456,8 @@ public void restoreDatabaseTest4() throws Exception { .setName(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString()) .setCreateTime(Timestamp.newBuilder().build()) .setRestoreInfo(RestoreInfo.newBuilder().build()) + .setVersionRetentionPeriod("versionRetentionPeriod-629783929") + .setEarliestVersionTime(Timestamp.newBuilder().build()) .build(); Operation resultOperation = Operation.newBuilder() diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/Backup.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/Backup.java index 0d9230003e1..935d46f59df 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/Backup.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/Backup.java @@ -138,6 +138,21 @@ private Backup( mutable_bitField0_ |= 0x00000001; } referencingDatabases_.add(s); + break; + } + case 74: + { + com.google.protobuf.Timestamp.Builder subBuilder = null; + if (versionTime_ != null) { + subBuilder = versionTime_.toBuilder(); + } + versionTime_ = + input.readMessage(com.google.protobuf.Timestamp.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(versionTime_); + versionTime_ = subBuilder.buildPartial(); + } + break; } default: @@ -393,6 +408,61 @@ public com.google.protobuf.ByteString getDatabaseBytes() { } } + public static final int VERSION_TIME_FIELD_NUMBER = 9; + private com.google.protobuf.Timestamp versionTime_; + /** + * + * + *
    +   * The backup will contain an externally consistent copy of the database at
    +   * the timestamp specified by `version_time`. If `version_time` is not
    +   * specified, the system will set `version_time` to the `create_time` of the
    +   * backup.
    +   * 
    + * + * .google.protobuf.Timestamp version_time = 9; + * + * @return Whether the versionTime field is set. + */ + @java.lang.Override + public boolean hasVersionTime() { + return versionTime_ != null; + } + /** + * + * + *
    +   * The backup will contain an externally consistent copy of the database at
    +   * the timestamp specified by `version_time`. If `version_time` is not
    +   * specified, the system will set `version_time` to the `create_time` of the
    +   * backup.
    +   * 
    + * + * .google.protobuf.Timestamp version_time = 9; + * + * @return The versionTime. + */ + @java.lang.Override + public com.google.protobuf.Timestamp getVersionTime() { + return versionTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : versionTime_; + } + /** + * + * + *
    +   * The backup will contain an externally consistent copy of the database at
    +   * the timestamp specified by `version_time`. If `version_time` is not
    +   * specified, the system will set `version_time` to the `create_time` of the
    +   * backup.
    +   * 
    + * + * .google.protobuf.Timestamp version_time = 9; + */ + @java.lang.Override + public com.google.protobuf.TimestampOrBuilder getVersionTimeOrBuilder() { + return getVersionTime(); + } + public static final int EXPIRE_TIME_FIELD_NUMBER = 3; private com.google.protobuf.Timestamp expireTime_; /** @@ -529,10 +599,9 @@ public com.google.protobuf.ByteString getNameBytes() { * * *
    -   * Output only. The backup will contain an externally consistent
    -   * copy of the database at the timestamp specified by
    -   * `create_time`. `create_time` is approximately the time the
    -   * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +   * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +   * request is received. If the request does not specify `version_time`, the
    +   * `version_time` of the backup will be equivalent to the `create_time`.
        * 
    * * .google.protobuf.Timestamp create_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; @@ -548,10 +617,9 @@ public boolean hasCreateTime() { * * *
    -   * Output only. The backup will contain an externally consistent
    -   * copy of the database at the timestamp specified by
    -   * `create_time`. `create_time` is approximately the time the
    -   * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +   * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +   * request is received. If the request does not specify `version_time`, the
    +   * `version_time` of the backup will be equivalent to the `create_time`.
        * 
    * * .google.protobuf.Timestamp create_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; @@ -567,10 +635,9 @@ public com.google.protobuf.Timestamp getCreateTime() { * * *
    -   * Output only. The backup will contain an externally consistent
    -   * copy of the database at the timestamp specified by
    -   * `create_time`. `create_time` is approximately the time the
    -   * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +   * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +   * request is received. If the request does not specify `version_time`, the
    +   * `version_time` of the backup will be equivalent to the `create_time`.
        * 
    * * .google.protobuf.Timestamp create_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; @@ -764,6 +831,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io com.google.protobuf.GeneratedMessageV3.writeString( output, 7, referencingDatabases_.getRaw(i)); } + if (versionTime_ != null) { + output.writeMessage(9, getVersionTime()); + } unknownFields.writeTo(output); } @@ -799,6 +869,9 @@ public int getSerializedSize() { size += dataSize; size += 1 * getReferencingDatabasesList().size(); } + if (versionTime_ != null) { + size += com.google.protobuf.CodedOutputStream.computeMessageSize(9, getVersionTime()); + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -816,6 +889,10 @@ public boolean equals(final java.lang.Object obj) { (com.google.spanner.admin.database.v1.Backup) obj; if (!getDatabase().equals(other.getDatabase())) return false; + if (hasVersionTime() != other.hasVersionTime()) return false; + if (hasVersionTime()) { + if (!getVersionTime().equals(other.getVersionTime())) return false; + } if (hasExpireTime() != other.hasExpireTime()) return false; if (hasExpireTime()) { if (!getExpireTime().equals(other.getExpireTime())) return false; @@ -841,6 +918,10 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + DATABASE_FIELD_NUMBER; hash = (53 * hash) + getDatabase().hashCode(); + if (hasVersionTime()) { + hash = (37 * hash) + VERSION_TIME_FIELD_NUMBER; + hash = (53 * hash) + getVersionTime().hashCode(); + } if (hasExpireTime()) { hash = (37 * hash) + EXPIRE_TIME_FIELD_NUMBER; hash = (53 * hash) + getExpireTime().hashCode(); @@ -1006,6 +1087,12 @@ public Builder clear() { super.clear(); database_ = ""; + if (versionTimeBuilder_ == null) { + versionTime_ = null; + } else { + versionTime_ = null; + versionTimeBuilder_ = null; + } if (expireTimeBuilder_ == null) { expireTime_ = null; } else { @@ -1055,6 +1142,11 @@ public com.google.spanner.admin.database.v1.Backup buildPartial() { new com.google.spanner.admin.database.v1.Backup(this); int from_bitField0_ = bitField0_; result.database_ = database_; + if (versionTimeBuilder_ == null) { + result.versionTime_ = versionTime_; + } else { + result.versionTime_ = versionTimeBuilder_.build(); + } if (expireTimeBuilder_ == null) { result.expireTime_ = expireTime_; } else { @@ -1126,6 +1218,9 @@ public Builder mergeFrom(com.google.spanner.admin.database.v1.Backup other) { database_ = other.database_; onChanged(); } + if (other.hasVersionTime()) { + mergeVersionTime(other.getVersionTime()); + } if (other.hasExpireTime()) { mergeExpireTime(other.getExpireTime()); } @@ -1309,6 +1404,218 @@ public Builder setDatabaseBytes(com.google.protobuf.ByteString value) { return this; } + private com.google.protobuf.Timestamp versionTime_; + private com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Timestamp, + com.google.protobuf.Timestamp.Builder, + com.google.protobuf.TimestampOrBuilder> + versionTimeBuilder_; + /** + * + * + *
    +     * The backup will contain an externally consistent copy of the database at
    +     * the timestamp specified by `version_time`. If `version_time` is not
    +     * specified, the system will set `version_time` to the `create_time` of the
    +     * backup.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 9; + * + * @return Whether the versionTime field is set. + */ + public boolean hasVersionTime() { + return versionTimeBuilder_ != null || versionTime_ != null; + } + /** + * + * + *
    +     * The backup will contain an externally consistent copy of the database at
    +     * the timestamp specified by `version_time`. If `version_time` is not
    +     * specified, the system will set `version_time` to the `create_time` of the
    +     * backup.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 9; + * + * @return The versionTime. + */ + public com.google.protobuf.Timestamp getVersionTime() { + if (versionTimeBuilder_ == null) { + return versionTime_ == null + ? com.google.protobuf.Timestamp.getDefaultInstance() + : versionTime_; + } else { + return versionTimeBuilder_.getMessage(); + } + } + /** + * + * + *
    +     * The backup will contain an externally consistent copy of the database at
    +     * the timestamp specified by `version_time`. If `version_time` is not
    +     * specified, the system will set `version_time` to the `create_time` of the
    +     * backup.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 9; + */ + public Builder setVersionTime(com.google.protobuf.Timestamp value) { + if (versionTimeBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + versionTime_ = value; + onChanged(); + } else { + versionTimeBuilder_.setMessage(value); + } + + return this; + } + /** + * + * + *
    +     * The backup will contain an externally consistent copy of the database at
    +     * the timestamp specified by `version_time`. If `version_time` is not
    +     * specified, the system will set `version_time` to the `create_time` of the
    +     * backup.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 9; + */ + public Builder setVersionTime(com.google.protobuf.Timestamp.Builder builderForValue) { + if (versionTimeBuilder_ == null) { + versionTime_ = builderForValue.build(); + onChanged(); + } else { + versionTimeBuilder_.setMessage(builderForValue.build()); + } + + return this; + } + /** + * + * + *
    +     * The backup will contain an externally consistent copy of the database at
    +     * the timestamp specified by `version_time`. If `version_time` is not
    +     * specified, the system will set `version_time` to the `create_time` of the
    +     * backup.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 9; + */ + public Builder mergeVersionTime(com.google.protobuf.Timestamp value) { + if (versionTimeBuilder_ == null) { + if (versionTime_ != null) { + versionTime_ = + com.google.protobuf.Timestamp.newBuilder(versionTime_) + .mergeFrom(value) + .buildPartial(); + } else { + versionTime_ = value; + } + onChanged(); + } else { + versionTimeBuilder_.mergeFrom(value); + } + + return this; + } + /** + * + * + *
    +     * The backup will contain an externally consistent copy of the database at
    +     * the timestamp specified by `version_time`. If `version_time` is not
    +     * specified, the system will set `version_time` to the `create_time` of the
    +     * backup.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 9; + */ + public Builder clearVersionTime() { + if (versionTimeBuilder_ == null) { + versionTime_ = null; + onChanged(); + } else { + versionTime_ = null; + versionTimeBuilder_ = null; + } + + return this; + } + /** + * + * + *
    +     * The backup will contain an externally consistent copy of the database at
    +     * the timestamp specified by `version_time`. If `version_time` is not
    +     * specified, the system will set `version_time` to the `create_time` of the
    +     * backup.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 9; + */ + public com.google.protobuf.Timestamp.Builder getVersionTimeBuilder() { + + onChanged(); + return getVersionTimeFieldBuilder().getBuilder(); + } + /** + * + * + *
    +     * The backup will contain an externally consistent copy of the database at
    +     * the timestamp specified by `version_time`. If `version_time` is not
    +     * specified, the system will set `version_time` to the `create_time` of the
    +     * backup.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 9; + */ + public com.google.protobuf.TimestampOrBuilder getVersionTimeOrBuilder() { + if (versionTimeBuilder_ != null) { + return versionTimeBuilder_.getMessageOrBuilder(); + } else { + return versionTime_ == null + ? com.google.protobuf.Timestamp.getDefaultInstance() + : versionTime_; + } + } + /** + * + * + *
    +     * The backup will contain an externally consistent copy of the database at
    +     * the timestamp specified by `version_time`. If `version_time` is not
    +     * specified, the system will set `version_time` to the `create_time` of the
    +     * backup.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 9; + */ + private com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Timestamp, + com.google.protobuf.Timestamp.Builder, + com.google.protobuf.TimestampOrBuilder> + getVersionTimeFieldBuilder() { + if (versionTimeBuilder_ == null) { + versionTimeBuilder_ = + new com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Timestamp, + com.google.protobuf.Timestamp.Builder, + com.google.protobuf.TimestampOrBuilder>( + getVersionTime(), getParentForChildren(), isClean()); + versionTime_ = null; + } + return versionTimeBuilder_; + } + private com.google.protobuf.Timestamp expireTime_; private com.google.protobuf.SingleFieldBuilderV3< com.google.protobuf.Timestamp, @@ -1703,10 +2010,9 @@ public Builder setNameBytes(com.google.protobuf.ByteString value) { * * *
    -     * Output only. The backup will contain an externally consistent
    -     * copy of the database at the timestamp specified by
    -     * `create_time`. `create_time` is approximately the time the
    -     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +     * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +     * request is received. If the request does not specify `version_time`, the
    +     * `version_time` of the backup will be equivalent to the `create_time`.
          * 
    * * @@ -1722,10 +2028,9 @@ public boolean hasCreateTime() { * * *
    -     * Output only. The backup will contain an externally consistent
    -     * copy of the database at the timestamp specified by
    -     * `create_time`. `create_time` is approximately the time the
    -     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +     * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +     * request is received. If the request does not specify `version_time`, the
    +     * `version_time` of the backup will be equivalent to the `create_time`.
          * 
    * * @@ -1747,10 +2052,9 @@ public com.google.protobuf.Timestamp getCreateTime() { * * *
    -     * Output only. The backup will contain an externally consistent
    -     * copy of the database at the timestamp specified by
    -     * `create_time`. `create_time` is approximately the time the
    -     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +     * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +     * request is received. If the request does not specify `version_time`, the
    +     * `version_time` of the backup will be equivalent to the `create_time`.
          * 
    * * @@ -1774,10 +2078,9 @@ public Builder setCreateTime(com.google.protobuf.Timestamp value) { * * *
    -     * Output only. The backup will contain an externally consistent
    -     * copy of the database at the timestamp specified by
    -     * `create_time`. `create_time` is approximately the time the
    -     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +     * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +     * request is received. If the request does not specify `version_time`, the
    +     * `version_time` of the backup will be equivalent to the `create_time`.
          * 
    * * @@ -1798,10 +2101,9 @@ public Builder setCreateTime(com.google.protobuf.Timestamp.Builder builderForVal * * *
    -     * Output only. The backup will contain an externally consistent
    -     * copy of the database at the timestamp specified by
    -     * `create_time`. `create_time` is approximately the time the
    -     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +     * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +     * request is received. If the request does not specify `version_time`, the
    +     * `version_time` of the backup will be equivalent to the `create_time`.
          * 
    * * @@ -1827,10 +2129,9 @@ public Builder mergeCreateTime(com.google.protobuf.Timestamp value) { * * *
    -     * Output only. The backup will contain an externally consistent
    -     * copy of the database at the timestamp specified by
    -     * `create_time`. `create_time` is approximately the time the
    -     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +     * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +     * request is received. If the request does not specify `version_time`, the
    +     * `version_time` of the backup will be equivalent to the `create_time`.
          * 
    * * @@ -1852,10 +2153,9 @@ public Builder clearCreateTime() { * * *
    -     * Output only. The backup will contain an externally consistent
    -     * copy of the database at the timestamp specified by
    -     * `create_time`. `create_time` is approximately the time the
    -     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +     * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +     * request is received. If the request does not specify `version_time`, the
    +     * `version_time` of the backup will be equivalent to the `create_time`.
          * 
    * * @@ -1871,10 +2171,9 @@ public com.google.protobuf.Timestamp.Builder getCreateTimeBuilder() { * * *
    -     * Output only. The backup will contain an externally consistent
    -     * copy of the database at the timestamp specified by
    -     * `create_time`. `create_time` is approximately the time the
    -     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +     * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +     * request is received. If the request does not specify `version_time`, the
    +     * `version_time` of the backup will be equivalent to the `create_time`.
          * 
    * * @@ -1894,10 +2193,9 @@ public com.google.protobuf.TimestampOrBuilder getCreateTimeOrBuilder() { * * *
    -     * Output only. The backup will contain an externally consistent
    -     * copy of the database at the timestamp specified by
    -     * `create_time`. `create_time` is approximately the time the
    -     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +     * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +     * request is received. If the request does not specify `version_time`, the
    +     * `version_time` of the backup will be equivalent to the `create_time`.
          * 
    * * diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupInfo.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupInfo.java index 99bc0f10191..56ce510e170 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupInfo.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupInfo.java @@ -98,6 +98,21 @@ private BackupInfo( java.lang.String s = input.readStringRequireUtf8(); sourceDatabase_ = s; + break; + } + case 34: + { + com.google.protobuf.Timestamp.Builder subBuilder = null; + if (versionTime_ != null) { + subBuilder = versionTime_.toBuilder(); + } + versionTime_ = + input.readMessage(com.google.protobuf.Timestamp.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(versionTime_); + versionTime_ = subBuilder.buildPartial(); + } + break; } default: @@ -143,7 +158,7 @@ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { * Name of the backup. *
  • * - * string backup = 1; + * string backup = 1 [(.google.api.resource_reference) = { ... } * * @return The backup. */ @@ -166,7 +181,7 @@ public java.lang.String getBackup() { * Name of the backup. *
    * - * string backup = 1; + * string backup = 1 [(.google.api.resource_reference) = { ... } * * @return The bytes for backup. */ @@ -183,14 +198,72 @@ public com.google.protobuf.ByteString getBackupBytes() { } } + public static final int VERSION_TIME_FIELD_NUMBER = 4; + private com.google.protobuf.Timestamp versionTime_; + /** + * + * + *
    +   * The backup contains an externally consistent copy of `source_database` at
    +   * the timestamp specified by `version_time`. If the
    +   * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +   * `version_time`, the `version_time` of the backup is equivalent to the
    +   * `create_time`.
    +   * 
    + * + * .google.protobuf.Timestamp version_time = 4; + * + * @return Whether the versionTime field is set. + */ + @java.lang.Override + public boolean hasVersionTime() { + return versionTime_ != null; + } + /** + * + * + *
    +   * The backup contains an externally consistent copy of `source_database` at
    +   * the timestamp specified by `version_time`. If the
    +   * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +   * `version_time`, the `version_time` of the backup is equivalent to the
    +   * `create_time`.
    +   * 
    + * + * .google.protobuf.Timestamp version_time = 4; + * + * @return The versionTime. + */ + @java.lang.Override + public com.google.protobuf.Timestamp getVersionTime() { + return versionTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : versionTime_; + } + /** + * + * + *
    +   * The backup contains an externally consistent copy of `source_database` at
    +   * the timestamp specified by `version_time`. If the
    +   * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +   * `version_time`, the `version_time` of the backup is equivalent to the
    +   * `create_time`.
    +   * 
    + * + * .google.protobuf.Timestamp version_time = 4; + */ + @java.lang.Override + public com.google.protobuf.TimestampOrBuilder getVersionTimeOrBuilder() { + return getVersionTime(); + } + public static final int CREATE_TIME_FIELD_NUMBER = 2; private com.google.protobuf.Timestamp createTime_; /** * * *
    -   * The backup contains an externally consistent copy of `source_database` at
    -   * the timestamp specified by `create_time`.
    +   * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +   * received.
        * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -205,8 +278,8 @@ public boolean hasCreateTime() { * * *
    -   * The backup contains an externally consistent copy of `source_database` at
    -   * the timestamp specified by `create_time`.
    +   * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +   * received.
        * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -221,8 +294,8 @@ public com.google.protobuf.Timestamp getCreateTime() { * * *
    -   * The backup contains an externally consistent copy of `source_database` at
    -   * the timestamp specified by `create_time`.
    +   * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +   * received.
        * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -241,7 +314,7 @@ public com.google.protobuf.TimestampOrBuilder getCreateTimeOrBuilder() { * Name of the database the backup was created from. * * - * string source_database = 3; + * string source_database = 3 [(.google.api.resource_reference) = { ... } * * @return The sourceDatabase. */ @@ -264,7 +337,7 @@ public java.lang.String getSourceDatabase() { * Name of the database the backup was created from. * * - * string source_database = 3; + * string source_database = 3 [(.google.api.resource_reference) = { ... } * * @return The bytes for sourceDatabase. */ @@ -304,6 +377,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io if (!getSourceDatabaseBytes().isEmpty()) { com.google.protobuf.GeneratedMessageV3.writeString(output, 3, sourceDatabase_); } + if (versionTime_ != null) { + output.writeMessage(4, getVersionTime()); + } unknownFields.writeTo(output); } @@ -322,6 +398,9 @@ public int getSerializedSize() { if (!getSourceDatabaseBytes().isEmpty()) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, sourceDatabase_); } + if (versionTime_ != null) { + size += com.google.protobuf.CodedOutputStream.computeMessageSize(4, getVersionTime()); + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -339,6 +418,10 @@ public boolean equals(final java.lang.Object obj) { (com.google.spanner.admin.database.v1.BackupInfo) obj; if (!getBackup().equals(other.getBackup())) return false; + if (hasVersionTime() != other.hasVersionTime()) return false; + if (hasVersionTime()) { + if (!getVersionTime().equals(other.getVersionTime())) return false; + } if (hasCreateTime() != other.hasCreateTime()) return false; if (hasCreateTime()) { if (!getCreateTime().equals(other.getCreateTime())) return false; @@ -357,6 +440,10 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + BACKUP_FIELD_NUMBER; hash = (53 * hash) + getBackup().hashCode(); + if (hasVersionTime()) { + hash = (37 * hash) + VERSION_TIME_FIELD_NUMBER; + hash = (53 * hash) + getVersionTime().hashCode(); + } if (hasCreateTime()) { hash = (37 * hash) + CREATE_TIME_FIELD_NUMBER; hash = (53 * hash) + getCreateTime().hashCode(); @@ -510,6 +597,12 @@ public Builder clear() { super.clear(); backup_ = ""; + if (versionTimeBuilder_ == null) { + versionTime_ = null; + } else { + versionTime_ = null; + versionTimeBuilder_ = null; + } if (createTimeBuilder_ == null) { createTime_ = null; } else { @@ -546,6 +639,11 @@ public com.google.spanner.admin.database.v1.BackupInfo buildPartial() { com.google.spanner.admin.database.v1.BackupInfo result = new com.google.spanner.admin.database.v1.BackupInfo(this); result.backup_ = backup_; + if (versionTimeBuilder_ == null) { + result.versionTime_ = versionTime_; + } else { + result.versionTime_ = versionTimeBuilder_.build(); + } if (createTimeBuilder_ == null) { result.createTime_ = createTime_; } else { @@ -606,6 +704,9 @@ public Builder mergeFrom(com.google.spanner.admin.database.v1.BackupInfo other) backup_ = other.backup_; onChanged(); } + if (other.hasVersionTime()) { + mergeVersionTime(other.getVersionTime()); + } if (other.hasCreateTime()) { mergeCreateTime(other.getCreateTime()); } @@ -650,7 +751,7 @@ public Builder mergeFrom( * Name of the backup. * * - * string backup = 1; + * string backup = 1 [(.google.api.resource_reference) = { ... } * * @return The backup. */ @@ -672,7 +773,7 @@ public java.lang.String getBackup() { * Name of the backup. * * - * string backup = 1; + * string backup = 1 [(.google.api.resource_reference) = { ... } * * @return The bytes for backup. */ @@ -694,7 +795,7 @@ public com.google.protobuf.ByteString getBackupBytes() { * Name of the backup. * * - * string backup = 1; + * string backup = 1 [(.google.api.resource_reference) = { ... } * * @param value The backup to set. * @return This builder for chaining. @@ -715,7 +816,7 @@ public Builder setBackup(java.lang.String value) { * Name of the backup. * * - * string backup = 1; + * string backup = 1 [(.google.api.resource_reference) = { ... } * * @return This builder for chaining. */ @@ -732,7 +833,7 @@ public Builder clearBackup() { * Name of the backup. * * - * string backup = 1; + * string backup = 1 [(.google.api.resource_reference) = { ... } * * @param value The bytes for backup to set. * @return This builder for chaining. @@ -748,6 +849,227 @@ public Builder setBackupBytes(com.google.protobuf.ByteString value) { return this; } + private com.google.protobuf.Timestamp versionTime_; + private com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Timestamp, + com.google.protobuf.Timestamp.Builder, + com.google.protobuf.TimestampOrBuilder> + versionTimeBuilder_; + /** + * + * + *
    +     * The backup contains an externally consistent copy of `source_database` at
    +     * the timestamp specified by `version_time`. If the
    +     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +     * `version_time`, the `version_time` of the backup is equivalent to the
    +     * `create_time`.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 4; + * + * @return Whether the versionTime field is set. + */ + public boolean hasVersionTime() { + return versionTimeBuilder_ != null || versionTime_ != null; + } + /** + * + * + *
    +     * The backup contains an externally consistent copy of `source_database` at
    +     * the timestamp specified by `version_time`. If the
    +     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +     * `version_time`, the `version_time` of the backup is equivalent to the
    +     * `create_time`.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 4; + * + * @return The versionTime. + */ + public com.google.protobuf.Timestamp getVersionTime() { + if (versionTimeBuilder_ == null) { + return versionTime_ == null + ? com.google.protobuf.Timestamp.getDefaultInstance() + : versionTime_; + } else { + return versionTimeBuilder_.getMessage(); + } + } + /** + * + * + *
    +     * The backup contains an externally consistent copy of `source_database` at
    +     * the timestamp specified by `version_time`. If the
    +     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +     * `version_time`, the `version_time` of the backup is equivalent to the
    +     * `create_time`.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 4; + */ + public Builder setVersionTime(com.google.protobuf.Timestamp value) { + if (versionTimeBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + versionTime_ = value; + onChanged(); + } else { + versionTimeBuilder_.setMessage(value); + } + + return this; + } + /** + * + * + *
    +     * The backup contains an externally consistent copy of `source_database` at
    +     * the timestamp specified by `version_time`. If the
    +     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +     * `version_time`, the `version_time` of the backup is equivalent to the
    +     * `create_time`.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 4; + */ + public Builder setVersionTime(com.google.protobuf.Timestamp.Builder builderForValue) { + if (versionTimeBuilder_ == null) { + versionTime_ = builderForValue.build(); + onChanged(); + } else { + versionTimeBuilder_.setMessage(builderForValue.build()); + } + + return this; + } + /** + * + * + *
    +     * The backup contains an externally consistent copy of `source_database` at
    +     * the timestamp specified by `version_time`. If the
    +     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +     * `version_time`, the `version_time` of the backup is equivalent to the
    +     * `create_time`.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 4; + */ + public Builder mergeVersionTime(com.google.protobuf.Timestamp value) { + if (versionTimeBuilder_ == null) { + if (versionTime_ != null) { + versionTime_ = + com.google.protobuf.Timestamp.newBuilder(versionTime_) + .mergeFrom(value) + .buildPartial(); + } else { + versionTime_ = value; + } + onChanged(); + } else { + versionTimeBuilder_.mergeFrom(value); + } + + return this; + } + /** + * + * + *
    +     * The backup contains an externally consistent copy of `source_database` at
    +     * the timestamp specified by `version_time`. If the
    +     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +     * `version_time`, the `version_time` of the backup is equivalent to the
    +     * `create_time`.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 4; + */ + public Builder clearVersionTime() { + if (versionTimeBuilder_ == null) { + versionTime_ = null; + onChanged(); + } else { + versionTime_ = null; + versionTimeBuilder_ = null; + } + + return this; + } + /** + * + * + *
    +     * The backup contains an externally consistent copy of `source_database` at
    +     * the timestamp specified by `version_time`. If the
    +     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +     * `version_time`, the `version_time` of the backup is equivalent to the
    +     * `create_time`.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 4; + */ + public com.google.protobuf.Timestamp.Builder getVersionTimeBuilder() { + + onChanged(); + return getVersionTimeFieldBuilder().getBuilder(); + } + /** + * + * + *
    +     * The backup contains an externally consistent copy of `source_database` at
    +     * the timestamp specified by `version_time`. If the
    +     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +     * `version_time`, the `version_time` of the backup is equivalent to the
    +     * `create_time`.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 4; + */ + public com.google.protobuf.TimestampOrBuilder getVersionTimeOrBuilder() { + if (versionTimeBuilder_ != null) { + return versionTimeBuilder_.getMessageOrBuilder(); + } else { + return versionTime_ == null + ? com.google.protobuf.Timestamp.getDefaultInstance() + : versionTime_; + } + } + /** + * + * + *
    +     * The backup contains an externally consistent copy of `source_database` at
    +     * the timestamp specified by `version_time`. If the
    +     * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +     * `version_time`, the `version_time` of the backup is equivalent to the
    +     * `create_time`.
    +     * 
    + * + * .google.protobuf.Timestamp version_time = 4; + */ + private com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Timestamp, + com.google.protobuf.Timestamp.Builder, + com.google.protobuf.TimestampOrBuilder> + getVersionTimeFieldBuilder() { + if (versionTimeBuilder_ == null) { + versionTimeBuilder_ = + new com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Timestamp, + com.google.protobuf.Timestamp.Builder, + com.google.protobuf.TimestampOrBuilder>( + getVersionTime(), getParentForChildren(), isClean()); + versionTime_ = null; + } + return versionTimeBuilder_; + } + private com.google.protobuf.Timestamp createTime_; private com.google.protobuf.SingleFieldBuilderV3< com.google.protobuf.Timestamp, @@ -758,8 +1080,8 @@ public Builder setBackupBytes(com.google.protobuf.ByteString value) { * * *
    -     * The backup contains an externally consistent copy of `source_database` at
    -     * the timestamp specified by `create_time`.
    +     * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +     * received.
          * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -773,8 +1095,8 @@ public boolean hasCreateTime() { * * *
    -     * The backup contains an externally consistent copy of `source_database` at
    -     * the timestamp specified by `create_time`.
    +     * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +     * received.
          * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -794,8 +1116,8 @@ public com.google.protobuf.Timestamp getCreateTime() { * * *
    -     * The backup contains an externally consistent copy of `source_database` at
    -     * the timestamp specified by `create_time`.
    +     * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +     * received.
          * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -817,8 +1139,8 @@ public Builder setCreateTime(com.google.protobuf.Timestamp value) { * * *
    -     * The backup contains an externally consistent copy of `source_database` at
    -     * the timestamp specified by `create_time`.
    +     * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +     * received.
          * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -837,8 +1159,8 @@ public Builder setCreateTime(com.google.protobuf.Timestamp.Builder builderForVal * * *
    -     * The backup contains an externally consistent copy of `source_database` at
    -     * the timestamp specified by `create_time`.
    +     * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +     * received.
          * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -862,8 +1184,8 @@ public Builder mergeCreateTime(com.google.protobuf.Timestamp value) { * * *
    -     * The backup contains an externally consistent copy of `source_database` at
    -     * the timestamp specified by `create_time`.
    +     * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +     * received.
          * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -883,8 +1205,8 @@ public Builder clearCreateTime() { * * *
    -     * The backup contains an externally consistent copy of `source_database` at
    -     * the timestamp specified by `create_time`.
    +     * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +     * received.
          * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -898,8 +1220,8 @@ public com.google.protobuf.Timestamp.Builder getCreateTimeBuilder() { * * *
    -     * The backup contains an externally consistent copy of `source_database` at
    -     * the timestamp specified by `create_time`.
    +     * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +     * received.
          * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -917,8 +1239,8 @@ public com.google.protobuf.TimestampOrBuilder getCreateTimeOrBuilder() { * * *
    -     * The backup contains an externally consistent copy of `source_database` at
    -     * the timestamp specified by `create_time`.
    +     * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +     * received.
          * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -948,7 +1270,7 @@ public com.google.protobuf.TimestampOrBuilder getCreateTimeOrBuilder() { * Name of the database the backup was created from. * * - * string source_database = 3; + * string source_database = 3 [(.google.api.resource_reference) = { ... } * * @return The sourceDatabase. */ @@ -970,7 +1292,7 @@ public java.lang.String getSourceDatabase() { * Name of the database the backup was created from. * * - * string source_database = 3; + * string source_database = 3 [(.google.api.resource_reference) = { ... } * * @return The bytes for sourceDatabase. */ @@ -992,7 +1314,7 @@ public com.google.protobuf.ByteString getSourceDatabaseBytes() { * Name of the database the backup was created from. * * - * string source_database = 3; + * string source_database = 3 [(.google.api.resource_reference) = { ... } * * @param value The sourceDatabase to set. * @return This builder for chaining. @@ -1013,7 +1335,7 @@ public Builder setSourceDatabase(java.lang.String value) { * Name of the database the backup was created from. * * - * string source_database = 3; + * string source_database = 3 [(.google.api.resource_reference) = { ... } * * @return This builder for chaining. */ @@ -1030,7 +1352,7 @@ public Builder clearSourceDatabase() { * Name of the database the backup was created from. * * - * string source_database = 3; + * string source_database = 3 [(.google.api.resource_reference) = { ... } * * @param value The bytes for sourceDatabase to set. * @return This builder for chaining. diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupInfoOrBuilder.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupInfoOrBuilder.java index 250b63bc124..0d3766f1a33 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupInfoOrBuilder.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupInfoOrBuilder.java @@ -30,7 +30,7 @@ public interface BackupInfoOrBuilder * Name of the backup. * * - * string backup = 1; + * string backup = 1 [(.google.api.resource_reference) = { ... } * * @return The backup. */ @@ -42,7 +42,7 @@ public interface BackupInfoOrBuilder * Name of the backup. * * - * string backup = 1; + * string backup = 1 [(.google.api.resource_reference) = { ... } * * @return The bytes for backup. */ @@ -53,7 +53,54 @@ public interface BackupInfoOrBuilder * *
        * The backup contains an externally consistent copy of `source_database` at
    -   * the timestamp specified by `create_time`.
    +   * the timestamp specified by `version_time`. If the
    +   * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +   * `version_time`, the `version_time` of the backup is equivalent to the
    +   * `create_time`.
    +   * 
    + * + * .google.protobuf.Timestamp version_time = 4; + * + * @return Whether the versionTime field is set. + */ + boolean hasVersionTime(); + /** + * + * + *
    +   * The backup contains an externally consistent copy of `source_database` at
    +   * the timestamp specified by `version_time`. If the
    +   * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +   * `version_time`, the `version_time` of the backup is equivalent to the
    +   * `create_time`.
    +   * 
    + * + * .google.protobuf.Timestamp version_time = 4; + * + * @return The versionTime. + */ + com.google.protobuf.Timestamp getVersionTime(); + /** + * + * + *
    +   * The backup contains an externally consistent copy of `source_database` at
    +   * the timestamp specified by `version_time`. If the
    +   * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify
    +   * `version_time`, the `version_time` of the backup is equivalent to the
    +   * `create_time`.
    +   * 
    + * + * .google.protobuf.Timestamp version_time = 4; + */ + com.google.protobuf.TimestampOrBuilder getVersionTimeOrBuilder(); + + /** + * + * + *
    +   * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +   * received.
        * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -65,8 +112,8 @@ public interface BackupInfoOrBuilder * * *
    -   * The backup contains an externally consistent copy of `source_database` at
    -   * the timestamp specified by `create_time`.
    +   * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +   * received.
        * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -78,8 +125,8 @@ public interface BackupInfoOrBuilder * * *
    -   * The backup contains an externally consistent copy of `source_database` at
    -   * the timestamp specified by `create_time`.
    +   * The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was
    +   * received.
        * 
    * * .google.protobuf.Timestamp create_time = 2; @@ -93,7 +140,7 @@ public interface BackupInfoOrBuilder * Name of the database the backup was created from. * * - * string source_database = 3; + * string source_database = 3 [(.google.api.resource_reference) = { ... } * * @return The sourceDatabase. */ @@ -105,7 +152,7 @@ public interface BackupInfoOrBuilder * Name of the database the backup was created from. * * - * string source_database = 3; + * string source_database = 3 [(.google.api.resource_reference) = { ... } * * @return The bytes for sourceDatabase. */ diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupOrBuilder.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupOrBuilder.java index 2b7f566b122..67cfab26fc9 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupOrBuilder.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupOrBuilder.java @@ -56,6 +56,50 @@ public interface BackupOrBuilder */ com.google.protobuf.ByteString getDatabaseBytes(); + /** + * + * + *
    +   * The backup will contain an externally consistent copy of the database at
    +   * the timestamp specified by `version_time`. If `version_time` is not
    +   * specified, the system will set `version_time` to the `create_time` of the
    +   * backup.
    +   * 
    + * + * .google.protobuf.Timestamp version_time = 9; + * + * @return Whether the versionTime field is set. + */ + boolean hasVersionTime(); + /** + * + * + *
    +   * The backup will contain an externally consistent copy of the database at
    +   * the timestamp specified by `version_time`. If `version_time` is not
    +   * specified, the system will set `version_time` to the `create_time` of the
    +   * backup.
    +   * 
    + * + * .google.protobuf.Timestamp version_time = 9; + * + * @return The versionTime. + */ + com.google.protobuf.Timestamp getVersionTime(); + /** + * + * + *
    +   * The backup will contain an externally consistent copy of the database at
    +   * the timestamp specified by `version_time`. If `version_time` is not
    +   * specified, the system will set `version_time` to the `create_time` of the
    +   * backup.
    +   * 
    + * + * .google.protobuf.Timestamp version_time = 9; + */ + com.google.protobuf.TimestampOrBuilder getVersionTimeOrBuilder(); + /** * * @@ -155,10 +199,9 @@ public interface BackupOrBuilder * * *
    -   * Output only. The backup will contain an externally consistent
    -   * copy of the database at the timestamp specified by
    -   * `create_time`. `create_time` is approximately the time the
    -   * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +   * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +   * request is received. If the request does not specify `version_time`, the
    +   * `version_time` of the backup will be equivalent to the `create_time`.
        * 
    * * .google.protobuf.Timestamp create_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; @@ -171,10 +214,9 @@ public interface BackupOrBuilder * * *
    -   * Output only. The backup will contain an externally consistent
    -   * copy of the database at the timestamp specified by
    -   * `create_time`. `create_time` is approximately the time the
    -   * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +   * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +   * request is received. If the request does not specify `version_time`, the
    +   * `version_time` of the backup will be equivalent to the `create_time`.
        * 
    * * .google.protobuf.Timestamp create_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; @@ -187,10 +229,9 @@ public interface BackupOrBuilder * * *
    -   * Output only. The backup will contain an externally consistent
    -   * copy of the database at the timestamp specified by
    -   * `create_time`. `create_time` is approximately the time the
    -   * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received.
    +   * Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]
    +   * request is received. If the request does not specify `version_time`, the
    +   * `version_time` of the backup will be equivalent to the `create_time`.
        * 
    * * .google.protobuf.Timestamp create_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupProto.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupProto.java index 2aaecf7bcd8..dd28586f35e 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupProto.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupProto.java @@ -88,56 +88,62 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "d_mask.proto\032\037google/protobuf/timestamp." + "proto\032-google/spanner/admin/database/v1/" + "common.proto\032\034google/api/annotations.pro" - + "to\"\315\003\n\006Backup\0226\n\010database\030\002 \001(\tB$\372A!\n\037sp" - + "anner.googleapis.com/Database\022/\n\013expire_" - + "time\030\003 \001(\0132\032.google.protobuf.Timestamp\022\014" - + "\n\004name\030\001 \001(\t\0224\n\013create_time\030\004 \001(\0132\032.goog" - + "le.protobuf.TimestampB\003\340A\003\022\027\n\nsize_bytes" - + "\030\005 \001(\003B\003\340A\003\022B\n\005state\030\006 \001(\0162..google.span" - + "ner.admin.database.v1.Backup.StateB\003\340A\003\022" - + "\"\n\025referencing_databases\030\007 \003(\tB\003\340A\003\"7\n\005S" - + "tate\022\025\n\021STATE_UNSPECIFIED\020\000\022\014\n\010CREATING\020" - + "\001\022\t\n\005READY\020\002:\\\352AY\n\035spanner.googleapis.co" - + "m/Backup\0228projects/{project}/instances/{" - + "instance}/backups/{backup}\"\245\001\n\023CreateBac" - + "kupRequest\0227\n\006parent\030\001 \001(\tB\'\340A\002\372A!\n\037span" - + "ner.googleapis.com/Instance\022\026\n\tbackup_id" - + "\030\002 \001(\tB\003\340A\002\022=\n\006backup\030\003 \001(\0132(.google.spa" - + "nner.admin.database.v1.BackupB\003\340A\002\"\256\001\n\024C" - + "reateBackupMetadata\022\014\n\004name\030\001 \001(\t\022\020\n\010dat" - + "abase\030\002 \001(\t\022E\n\010progress\030\003 \001(\01323.google.s" - + "panner.admin.database.v1.OperationProgre" - + "ss\022/\n\013cancel_time\030\004 \001(\0132\032.google.protobu" - + "f.Timestamp\"\212\001\n\023UpdateBackupRequest\022=\n\006b" - + "ackup\030\001 \001(\0132(.google.spanner.admin.datab" - + "ase.v1.BackupB\003\340A\002\0224\n\013update_mask\030\002 \001(\0132" - + "\032.google.protobuf.FieldMaskB\003\340A\002\"G\n\020GetB" + + "to\"\377\003\n\006Backup\0226\n\010database\030\002 \001(\tB$\372A!\n\037sp" + + "anner.googleapis.com/Database\0220\n\014version" + + "_time\030\t \001(\0132\032.google.protobuf.Timestamp\022" + + "/\n\013expire_time\030\003 \001(\0132\032.google.protobuf.T" + + "imestamp\022\014\n\004name\030\001 \001(\t\0224\n\013create_time\030\004 " + + "\001(\0132\032.google.protobuf.TimestampB\003\340A\003\022\027\n\n" + + "size_bytes\030\005 \001(\003B\003\340A\003\022B\n\005state\030\006 \001(\0162..g" + + "oogle.spanner.admin.database.v1.Backup.S" + + "tateB\003\340A\003\022\"\n\025referencing_databases\030\007 \003(\t" + + "B\003\340A\003\"7\n\005State\022\025\n\021STATE_UNSPECIFIED\020\000\022\014\n" + + "\010CREATING\020\001\022\t\n\005READY\020\002:\\\352AY\n\035spanner.goo" + + "gleapis.com/Backup\0228projects/{project}/i" + + "nstances/{instance}/backups/{backup}\"\245\001\n" + + "\023CreateBackupRequest\0227\n\006parent\030\001 \001(\tB\'\340A" + + "\002\372A!\n\037spanner.googleapis.com/Instance\022\026\n" + + "\tbackup_id\030\002 \001(\tB\003\340A\002\022=\n\006backup\030\003 \001(\0132(." + + "google.spanner.admin.database.v1.BackupB" + + "\003\340A\002\"\370\001\n\024CreateBackupMetadata\0220\n\004name\030\001 " + + "\001(\tB\"\372A\037\n\035spanner.googleapis.com/Backup\022" + + "6\n\010database\030\002 \001(\tB$\372A!\n\037spanner.googleap" + + "is.com/Database\022E\n\010progress\030\003 \001(\01323.goog" + + "le.spanner.admin.database.v1.OperationPr" + + "ogress\022/\n\013cancel_time\030\004 \001(\0132\032.google.pro" + + "tobuf.Timestamp\"\212\001\n\023UpdateBackupRequest\022" + + "=\n\006backup\030\001 \001(\0132(.google.spanner.admin.d" + + "atabase.v1.BackupB\003\340A\002\0224\n\013update_mask\030\002 " + + "\001(\0132\032.google.protobuf.FieldMaskB\003\340A\002\"G\n\020" + + "GetBackupRequest\0223\n\004name\030\001 \001(\tB%\340A\002\372A\037\n\035" + + "spanner.googleapis.com/Backup\"J\n\023DeleteB" + "ackupRequest\0223\n\004name\030\001 \001(\tB%\340A\002\372A\037\n\035span" - + "ner.googleapis.com/Backup\"J\n\023DeleteBacku" - + "pRequest\0223\n\004name\030\001 \001(\tB%\340A\002\372A\037\n\035spanner." - + "googleapis.com/Backup\"\204\001\n\022ListBackupsReq" - + "uest\0227\n\006parent\030\001 \001(\tB\'\340A\002\372A!\n\037spanner.go" - + "ogleapis.com/Instance\022\016\n\006filter\030\002 \001(\t\022\021\n" - + "\tpage_size\030\003 \001(\005\022\022\n\npage_token\030\004 \001(\t\"i\n\023" - + "ListBackupsResponse\0229\n\007backups\030\001 \003(\0132(.g" - + "oogle.spanner.admin.database.v1.Backup\022\027" - + "\n\017next_page_token\030\002 \001(\t\"\215\001\n\033ListBackupOp" - + "erationsRequest\0227\n\006parent\030\001 \001(\tB\'\340A\002\372A!\n" - + "\037spanner.googleapis.com/Instance\022\016\n\006filt" - + "er\030\002 \001(\t\022\021\n\tpage_size\030\003 \001(\005\022\022\n\npage_toke" - + "n\030\004 \001(\t\"j\n\034ListBackupOperationsResponse\022" - + "1\n\noperations\030\001 \003(\0132\035.google.longrunning" - + ".Operation\022\027\n\017next_page_token\030\002 \001(\t\"f\n\nB" - + "ackupInfo\022\016\n\006backup\030\001 \001(\t\022/\n\013create_time" - + "\030\002 \001(\0132\032.google.protobuf.Timestamp\022\027\n\017so" - + "urce_database\030\003 \001(\tB\377\001\n$com.google.spann" - + "er.admin.database.v1B\013BackupProtoP\001ZHgoo" - + "gle.golang.org/genproto/googleapis/spann" - + "er/admin/database/v1;database\252\002&Google.C" - + "loud.Spanner.Admin.Database.V1\312\002&Google\\" - + "Cloud\\Spanner\\Admin\\Database\\V1\352\002+Google" - + "::Cloud::Spanner::Admin::Database::V1b\006p" - + "roto3" + + "ner.googleapis.com/Backup\"\204\001\n\022ListBackup" + + "sRequest\0227\n\006parent\030\001 \001(\tB\'\340A\002\372A!\n\037spanne" + + "r.googleapis.com/Instance\022\016\n\006filter\030\002 \001(" + + "\t\022\021\n\tpage_size\030\003 \001(\005\022\022\n\npage_token\030\004 \001(\t" + + "\"i\n\023ListBackupsResponse\0229\n\007backups\030\001 \003(\013" + + "2(.google.spanner.admin.database.v1.Back" + + "up\022\027\n\017next_page_token\030\002 \001(\t\"\215\001\n\033ListBack" + + "upOperationsRequest\0227\n\006parent\030\001 \001(\tB\'\340A\002" + + "\372A!\n\037spanner.googleapis.com/Instance\022\016\n\006" + + "filter\030\002 \001(\t\022\021\n\tpage_size\030\003 \001(\005\022\022\n\npage_" + + "token\030\004 \001(\t\"j\n\034ListBackupOperationsRespo" + + "nse\0221\n\noperations\030\001 \003(\0132\035.google.longrun" + + "ning.Operation\022\027\n\017next_page_token\030\002 \001(\t\"" + + "\342\001\n\nBackupInfo\0222\n\006backup\030\001 \001(\tB\"\372A\037\n\035spa" + + "nner.googleapis.com/Backup\0220\n\014version_ti" + + "me\030\004 \001(\0132\032.google.protobuf.Timestamp\022/\n\013" + + "create_time\030\002 \001(\0132\032.google.protobuf.Time" + + "stamp\022=\n\017source_database\030\003 \001(\tB$\372A!\n\037spa" + + "nner.googleapis.com/DatabaseB\377\001\n$com.goo" + + "gle.spanner.admin.database.v1B\013BackupPro" + + "toP\001ZHgoogle.golang.org/genproto/googlea" + + "pis/spanner/admin/database/v1;database\252\002" + + "&Google.Cloud.Spanner.Admin.Database.V1\312" + + "\002&Google\\Cloud\\Spanner\\Admin\\Database\\V1" + + "\352\002+Google::Cloud::Spanner::Admin::Databa" + + "se::V1b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom( @@ -158,6 +164,7 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { internal_static_google_spanner_admin_database_v1_Backup_descriptor, new java.lang.String[] { "Database", + "VersionTime", "ExpireTime", "Name", "CreateTime", @@ -243,7 +250,7 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_google_spanner_admin_database_v1_BackupInfo_descriptor, new java.lang.String[] { - "Backup", "CreateTime", "SourceDatabase", + "Backup", "VersionTime", "CreateTime", "SourceDatabase", }); com.google.protobuf.ExtensionRegistry registry = com.google.protobuf.ExtensionRegistry.newInstance(); diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateBackupMetadata.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateBackupMetadata.java index a1b0cc35124..c4e23c96e69 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateBackupMetadata.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateBackupMetadata.java @@ -161,7 +161,7 @@ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { * The name of the backup being created. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The name. */ @@ -184,7 +184,7 @@ public java.lang.String getName() { * The name of the backup being created. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The bytes for name. */ @@ -210,7 +210,7 @@ public com.google.protobuf.ByteString getNameBytes() { * The name of the database the backup is created from. * * - * string database = 2; + * string database = 2 [(.google.api.resource_reference) = { ... } * * @return The database. */ @@ -233,7 +233,7 @@ public java.lang.String getDatabase() { * The name of the database the backup is created from. * * - * string database = 2; + * string database = 2 [(.google.api.resource_reference) = { ... } * * @return The bytes for database. */ @@ -777,7 +777,7 @@ public Builder mergeFrom( * The name of the backup being created. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The name. */ @@ -799,7 +799,7 @@ public java.lang.String getName() { * The name of the backup being created. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The bytes for name. */ @@ -821,7 +821,7 @@ public com.google.protobuf.ByteString getNameBytes() { * The name of the backup being created. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @param value The name to set. * @return This builder for chaining. @@ -842,7 +842,7 @@ public Builder setName(java.lang.String value) { * The name of the backup being created. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return This builder for chaining. */ @@ -859,7 +859,7 @@ public Builder clearName() { * The name of the backup being created. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @param value The bytes for name to set. * @return This builder for chaining. @@ -883,7 +883,7 @@ public Builder setNameBytes(com.google.protobuf.ByteString value) { * The name of the database the backup is created from. * * - * string database = 2; + * string database = 2 [(.google.api.resource_reference) = { ... } * * @return The database. */ @@ -905,7 +905,7 @@ public java.lang.String getDatabase() { * The name of the database the backup is created from. * * - * string database = 2; + * string database = 2 [(.google.api.resource_reference) = { ... } * * @return The bytes for database. */ @@ -927,7 +927,7 @@ public com.google.protobuf.ByteString getDatabaseBytes() { * The name of the database the backup is created from. * * - * string database = 2; + * string database = 2 [(.google.api.resource_reference) = { ... } * * @param value The database to set. * @return This builder for chaining. @@ -948,7 +948,7 @@ public Builder setDatabase(java.lang.String value) { * The name of the database the backup is created from. * * - * string database = 2; + * string database = 2 [(.google.api.resource_reference) = { ... } * * @return This builder for chaining. */ @@ -965,7 +965,7 @@ public Builder clearDatabase() { * The name of the database the backup is created from. * * - * string database = 2; + * string database = 2 [(.google.api.resource_reference) = { ... } * * @param value The bytes for database to set. * @return This builder for chaining. diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateBackupMetadataOrBuilder.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateBackupMetadataOrBuilder.java index 5034e3af02d..fcb89c03472 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateBackupMetadataOrBuilder.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateBackupMetadataOrBuilder.java @@ -30,7 +30,7 @@ public interface CreateBackupMetadataOrBuilder * The name of the backup being created. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The name. */ @@ -42,7 +42,7 @@ public interface CreateBackupMetadataOrBuilder * The name of the backup being created. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The bytes for name. */ @@ -55,7 +55,7 @@ public interface CreateBackupMetadataOrBuilder * The name of the database the backup is created from. * * - * string database = 2; + * string database = 2 [(.google.api.resource_reference) = { ... } * * @return The database. */ @@ -67,7 +67,7 @@ public interface CreateBackupMetadataOrBuilder * The name of the database the backup is created from. * * - * string database = 2; + * string database = 2 [(.google.api.resource_reference) = { ... } * * @return The bytes for database. */ diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/Database.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/Database.java index 768f470254d..f4c866c8f85 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/Database.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/Database.java @@ -40,6 +40,7 @@ private Database(com.google.protobuf.GeneratedMessageV3.Builder builder) { private Database() { name_ = ""; state_ = 0; + versionRetentionPeriod_ = ""; } @java.lang.Override @@ -114,6 +115,28 @@ private Database( restoreInfo_ = subBuilder.buildPartial(); } + break; + } + case 50: + { + java.lang.String s = input.readStringRequireUtf8(); + + versionRetentionPeriod_ = s; + break; + } + case 58: + { + com.google.protobuf.Timestamp.Builder subBuilder = null; + if (earliestVersionTime_ != null) { + subBuilder = earliestVersionTime_.toBuilder(); + } + earliestVersionTime_ = + input.readMessage(com.google.protobuf.Timestamp.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(earliestVersionTime_); + earliestVersionTime_ = subBuilder.buildPartial(); + } + break; } default: @@ -548,6 +571,120 @@ public com.google.spanner.admin.database.v1.RestoreInfoOrBuilder getRestoreInfoO return getRestoreInfo(); } + public static final int VERSION_RETENTION_PERIOD_FIELD_NUMBER = 6; + private volatile java.lang.Object versionRetentionPeriod_; + /** + * + * + *
    +   * Output only. The period in which Cloud Spanner retains all versions of data
    +   * for the database. This is the same as the value of version_retention_period
    +   * database option set using
    +   * [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl]. Defaults to 1 hour,
    +   * if not set.
    +   * 
    + * + * string version_retention_period = 6 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * @return The versionRetentionPeriod. + */ + @java.lang.Override + public java.lang.String getVersionRetentionPeriod() { + java.lang.Object ref = versionRetentionPeriod_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + versionRetentionPeriod_ = s; + return s; + } + } + /** + * + * + *
    +   * Output only. The period in which Cloud Spanner retains all versions of data
    +   * for the database. This is the same as the value of version_retention_period
    +   * database option set using
    +   * [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl]. Defaults to 1 hour,
    +   * if not set.
    +   * 
    + * + * string version_retention_period = 6 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * @return The bytes for versionRetentionPeriod. + */ + @java.lang.Override + public com.google.protobuf.ByteString getVersionRetentionPeriodBytes() { + java.lang.Object ref = versionRetentionPeriod_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + versionRetentionPeriod_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int EARLIEST_VERSION_TIME_FIELD_NUMBER = 7; + private com.google.protobuf.Timestamp earliestVersionTime_; + /** + * + * + *
    +   * Output only. Earliest timestamp at which older versions of the data can be
    +   * read.
    +   * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * + * @return Whether the earliestVersionTime field is set. + */ + @java.lang.Override + public boolean hasEarliestVersionTime() { + return earliestVersionTime_ != null; + } + /** + * + * + *
    +   * Output only. Earliest timestamp at which older versions of the data can be
    +   * read.
    +   * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * + * @return The earliestVersionTime. + */ + @java.lang.Override + public com.google.protobuf.Timestamp getEarliestVersionTime() { + return earliestVersionTime_ == null + ? com.google.protobuf.Timestamp.getDefaultInstance() + : earliestVersionTime_; + } + /** + * + * + *
    +   * Output only. Earliest timestamp at which older versions of the data can be
    +   * read.
    +   * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + */ + @java.lang.Override + public com.google.protobuf.TimestampOrBuilder getEarliestVersionTimeOrBuilder() { + return getEarliestVersionTime(); + } + private byte memoizedIsInitialized = -1; @java.lang.Override @@ -575,6 +712,12 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io if (restoreInfo_ != null) { output.writeMessage(4, getRestoreInfo()); } + if (!getVersionRetentionPeriodBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 6, versionRetentionPeriod_); + } + if (earliestVersionTime_ != null) { + output.writeMessage(7, getEarliestVersionTime()); + } unknownFields.writeTo(output); } @@ -597,6 +740,12 @@ public int getSerializedSize() { if (restoreInfo_ != null) { size += com.google.protobuf.CodedOutputStream.computeMessageSize(4, getRestoreInfo()); } + if (!getVersionRetentionPeriodBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(6, versionRetentionPeriod_); + } + if (earliestVersionTime_ != null) { + size += com.google.protobuf.CodedOutputStream.computeMessageSize(7, getEarliestVersionTime()); + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -623,6 +772,11 @@ public boolean equals(final java.lang.Object obj) { if (hasRestoreInfo()) { if (!getRestoreInfo().equals(other.getRestoreInfo())) return false; } + if (!getVersionRetentionPeriod().equals(other.getVersionRetentionPeriod())) return false; + if (hasEarliestVersionTime() != other.hasEarliestVersionTime()) return false; + if (hasEarliestVersionTime()) { + if (!getEarliestVersionTime().equals(other.getEarliestVersionTime())) return false; + } if (!unknownFields.equals(other.unknownFields)) return false; return true; } @@ -646,6 +800,12 @@ public int hashCode() { hash = (37 * hash) + RESTORE_INFO_FIELD_NUMBER; hash = (53 * hash) + getRestoreInfo().hashCode(); } + hash = (37 * hash) + VERSION_RETENTION_PERIOD_FIELD_NUMBER; + hash = (53 * hash) + getVersionRetentionPeriod().hashCode(); + if (hasEarliestVersionTime()) { + hash = (37 * hash) + EARLIEST_VERSION_TIME_FIELD_NUMBER; + hash = (53 * hash) + getEarliestVersionTime().hashCode(); + } hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; @@ -807,6 +967,14 @@ public Builder clear() { restoreInfo_ = null; restoreInfoBuilder_ = null; } + versionRetentionPeriod_ = ""; + + if (earliestVersionTimeBuilder_ == null) { + earliestVersionTime_ = null; + } else { + earliestVersionTime_ = null; + earliestVersionTimeBuilder_ = null; + } return this; } @@ -846,6 +1014,12 @@ public com.google.spanner.admin.database.v1.Database buildPartial() { } else { result.restoreInfo_ = restoreInfoBuilder_.build(); } + result.versionRetentionPeriod_ = versionRetentionPeriod_; + if (earliestVersionTimeBuilder_ == null) { + result.earliestVersionTime_ = earliestVersionTime_; + } else { + result.earliestVersionTime_ = earliestVersionTimeBuilder_.build(); + } onBuilt(); return result; } @@ -908,6 +1082,13 @@ public Builder mergeFrom(com.google.spanner.admin.database.v1.Database other) { if (other.hasRestoreInfo()) { mergeRestoreInfo(other.getRestoreInfo()); } + if (!other.getVersionRetentionPeriod().isEmpty()) { + versionRetentionPeriod_ = other.versionRetentionPeriod_; + onChanged(); + } + if (other.hasEarliestVersionTime()) { + mergeEarliestVersionTime(other.getEarliestVersionTime()); + } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; @@ -1580,6 +1761,349 @@ public com.google.spanner.admin.database.v1.RestoreInfoOrBuilder getRestoreInfoO return restoreInfoBuilder_; } + private java.lang.Object versionRetentionPeriod_ = ""; + /** + * + * + *
    +     * Output only. The period in which Cloud Spanner retains all versions of data
    +     * for the database. This is the same as the value of version_retention_period
    +     * database option set using
    +     * [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl]. Defaults to 1 hour,
    +     * if not set.
    +     * 
    + * + * string version_retention_period = 6 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * + * @return The versionRetentionPeriod. + */ + public java.lang.String getVersionRetentionPeriod() { + java.lang.Object ref = versionRetentionPeriod_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + versionRetentionPeriod_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * + * + *
    +     * Output only. The period in which Cloud Spanner retains all versions of data
    +     * for the database. This is the same as the value of version_retention_period
    +     * database option set using
    +     * [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl]. Defaults to 1 hour,
    +     * if not set.
    +     * 
    + * + * string version_retention_period = 6 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * + * @return The bytes for versionRetentionPeriod. + */ + public com.google.protobuf.ByteString getVersionRetentionPeriodBytes() { + java.lang.Object ref = versionRetentionPeriod_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + versionRetentionPeriod_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * + * + *
    +     * Output only. The period in which Cloud Spanner retains all versions of data
    +     * for the database. This is the same as the value of version_retention_period
    +     * database option set using
    +     * [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl]. Defaults to 1 hour,
    +     * if not set.
    +     * 
    + * + * string version_retention_period = 6 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * + * @param value The versionRetentionPeriod to set. + * @return This builder for chaining. + */ + public Builder setVersionRetentionPeriod(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + versionRetentionPeriod_ = value; + onChanged(); + return this; + } + /** + * + * + *
    +     * Output only. The period in which Cloud Spanner retains all versions of data
    +     * for the database. This is the same as the value of version_retention_period
    +     * database option set using
    +     * [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl]. Defaults to 1 hour,
    +     * if not set.
    +     * 
    + * + * string version_retention_period = 6 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * + * @return This builder for chaining. + */ + public Builder clearVersionRetentionPeriod() { + + versionRetentionPeriod_ = getDefaultInstance().getVersionRetentionPeriod(); + onChanged(); + return this; + } + /** + * + * + *
    +     * Output only. The period in which Cloud Spanner retains all versions of data
    +     * for the database. This is the same as the value of version_retention_period
    +     * database option set using
    +     * [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl]. Defaults to 1 hour,
    +     * if not set.
    +     * 
    + * + * string version_retention_period = 6 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * + * @param value The bytes for versionRetentionPeriod to set. + * @return This builder for chaining. + */ + public Builder setVersionRetentionPeriodBytes(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + versionRetentionPeriod_ = value; + onChanged(); + return this; + } + + private com.google.protobuf.Timestamp earliestVersionTime_; + private com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Timestamp, + com.google.protobuf.Timestamp.Builder, + com.google.protobuf.TimestampOrBuilder> + earliestVersionTimeBuilder_; + /** + * + * + *
    +     * Output only. Earliest timestamp at which older versions of the data can be
    +     * read.
    +     * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * + * @return Whether the earliestVersionTime field is set. + */ + public boolean hasEarliestVersionTime() { + return earliestVersionTimeBuilder_ != null || earliestVersionTime_ != null; + } + /** + * + * + *
    +     * Output only. Earliest timestamp at which older versions of the data can be
    +     * read.
    +     * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * + * @return The earliestVersionTime. + */ + public com.google.protobuf.Timestamp getEarliestVersionTime() { + if (earliestVersionTimeBuilder_ == null) { + return earliestVersionTime_ == null + ? com.google.protobuf.Timestamp.getDefaultInstance() + : earliestVersionTime_; + } else { + return earliestVersionTimeBuilder_.getMessage(); + } + } + /** + * + * + *
    +     * Output only. Earliest timestamp at which older versions of the data can be
    +     * read.
    +     * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + */ + public Builder setEarliestVersionTime(com.google.protobuf.Timestamp value) { + if (earliestVersionTimeBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + earliestVersionTime_ = value; + onChanged(); + } else { + earliestVersionTimeBuilder_.setMessage(value); + } + + return this; + } + /** + * + * + *
    +     * Output only. Earliest timestamp at which older versions of the data can be
    +     * read.
    +     * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + */ + public Builder setEarliestVersionTime(com.google.protobuf.Timestamp.Builder builderForValue) { + if (earliestVersionTimeBuilder_ == null) { + earliestVersionTime_ = builderForValue.build(); + onChanged(); + } else { + earliestVersionTimeBuilder_.setMessage(builderForValue.build()); + } + + return this; + } + /** + * + * + *
    +     * Output only. Earliest timestamp at which older versions of the data can be
    +     * read.
    +     * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + */ + public Builder mergeEarliestVersionTime(com.google.protobuf.Timestamp value) { + if (earliestVersionTimeBuilder_ == null) { + if (earliestVersionTime_ != null) { + earliestVersionTime_ = + com.google.protobuf.Timestamp.newBuilder(earliestVersionTime_) + .mergeFrom(value) + .buildPartial(); + } else { + earliestVersionTime_ = value; + } + onChanged(); + } else { + earliestVersionTimeBuilder_.mergeFrom(value); + } + + return this; + } + /** + * + * + *
    +     * Output only. Earliest timestamp at which older versions of the data can be
    +     * read.
    +     * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + */ + public Builder clearEarliestVersionTime() { + if (earliestVersionTimeBuilder_ == null) { + earliestVersionTime_ = null; + onChanged(); + } else { + earliestVersionTime_ = null; + earliestVersionTimeBuilder_ = null; + } + + return this; + } + /** + * + * + *
    +     * Output only. Earliest timestamp at which older versions of the data can be
    +     * read.
    +     * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + */ + public com.google.protobuf.Timestamp.Builder getEarliestVersionTimeBuilder() { + + onChanged(); + return getEarliestVersionTimeFieldBuilder().getBuilder(); + } + /** + * + * + *
    +     * Output only. Earliest timestamp at which older versions of the data can be
    +     * read.
    +     * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + */ + public com.google.protobuf.TimestampOrBuilder getEarliestVersionTimeOrBuilder() { + if (earliestVersionTimeBuilder_ != null) { + return earliestVersionTimeBuilder_.getMessageOrBuilder(); + } else { + return earliestVersionTime_ == null + ? com.google.protobuf.Timestamp.getDefaultInstance() + : earliestVersionTime_; + } + } + /** + * + * + *
    +     * Output only. Earliest timestamp at which older versions of the data can be
    +     * read.
    +     * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + */ + private com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Timestamp, + com.google.protobuf.Timestamp.Builder, + com.google.protobuf.TimestampOrBuilder> + getEarliestVersionTimeFieldBuilder() { + if (earliestVersionTimeBuilder_ == null) { + earliestVersionTimeBuilder_ = + new com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Timestamp, + com.google.protobuf.Timestamp.Builder, + com.google.protobuf.TimestampOrBuilder>( + getEarliestVersionTime(), getParentForChildren(), isClean()); + earliestVersionTime_ = null; + } + return earliestVersionTimeBuilder_; + } + @java.lang.Override public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DatabaseOrBuilder.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DatabaseOrBuilder.java index 4c4869319f2..9ca843fc93f 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DatabaseOrBuilder.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DatabaseOrBuilder.java @@ -166,4 +166,81 @@ public interface DatabaseOrBuilder *
    */ com.google.spanner.admin.database.v1.RestoreInfoOrBuilder getRestoreInfoOrBuilder(); + + /** + * + * + *
    +   * Output only. The period in which Cloud Spanner retains all versions of data
    +   * for the database. This is the same as the value of version_retention_period
    +   * database option set using
    +   * [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl]. Defaults to 1 hour,
    +   * if not set.
    +   * 
    + * + * string version_retention_period = 6 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * @return The versionRetentionPeriod. + */ + java.lang.String getVersionRetentionPeriod(); + /** + * + * + *
    +   * Output only. The period in which Cloud Spanner retains all versions of data
    +   * for the database. This is the same as the value of version_retention_period
    +   * database option set using
    +   * [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl]. Defaults to 1 hour,
    +   * if not set.
    +   * 
    + * + * string version_retention_period = 6 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * @return The bytes for versionRetentionPeriod. + */ + com.google.protobuf.ByteString getVersionRetentionPeriodBytes(); + + /** + * + * + *
    +   * Output only. Earliest timestamp at which older versions of the data can be
    +   * read.
    +   * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * + * @return Whether the earliestVersionTime field is set. + */ + boolean hasEarliestVersionTime(); + /** + * + * + *
    +   * Output only. Earliest timestamp at which older versions of the data can be
    +   * read.
    +   * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + * + * @return The earliestVersionTime. + */ + com.google.protobuf.Timestamp getEarliestVersionTime(); + /** + * + * + *
    +   * Output only. Earliest timestamp at which older versions of the data can be
    +   * read.
    +   * 
    + * + * + * .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; + * + */ + com.google.protobuf.TimestampOrBuilder getEarliestVersionTimeOrBuilder(); } diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetDatabaseDdlRequest.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetDatabaseDdlRequest.java index d730718fb37..f5bc73b7d83 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetDatabaseDdlRequest.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetDatabaseDdlRequest.java @@ -118,6 +118,8 @@ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { * *
        * Required. The database whose schema we wish to get.
    +   * Values are of the form
    +   * `projects/<project>/instances/<instance>/databases/<database>`
        * 
    * * @@ -143,6 +145,8 @@ public java.lang.String getDatabase() { * *
        * Required. The database whose schema we wish to get.
    +   * Values are of the form
    +   * `projects/<project>/instances/<instance>/databases/<database>`
        * 
    * * @@ -489,6 +493,8 @@ public Builder mergeFrom( * *
          * Required. The database whose schema we wish to get.
    +     * Values are of the form
    +     * `projects/<project>/instances/<instance>/databases/<database>`
          * 
    * * @@ -513,6 +519,8 @@ public java.lang.String getDatabase() { * *
          * Required. The database whose schema we wish to get.
    +     * Values are of the form
    +     * `projects/<project>/instances/<instance>/databases/<database>`
          * 
    * * @@ -537,6 +545,8 @@ public com.google.protobuf.ByteString getDatabaseBytes() { * *
          * Required. The database whose schema we wish to get.
    +     * Values are of the form
    +     * `projects/<project>/instances/<instance>/databases/<database>`
          * 
    * * @@ -560,6 +570,8 @@ public Builder setDatabase(java.lang.String value) { * *
          * Required. The database whose schema we wish to get.
    +     * Values are of the form
    +     * `projects/<project>/instances/<instance>/databases/<database>`
          * 
    * * @@ -579,6 +591,8 @@ public Builder clearDatabase() { * *
          * Required. The database whose schema we wish to get.
    +     * Values are of the form
    +     * `projects/<project>/instances/<instance>/databases/<database>`
          * 
    * * diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetDatabaseDdlRequestOrBuilder.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetDatabaseDdlRequestOrBuilder.java index 2e7da9cf5a4..2469460dc3e 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetDatabaseDdlRequestOrBuilder.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetDatabaseDdlRequestOrBuilder.java @@ -28,6 +28,8 @@ public interface GetDatabaseDdlRequestOrBuilder * *
        * Required. The database whose schema we wish to get.
    +   * Values are of the form
    +   * `projects/<project>/instances/<instance>/databases/<database>`
        * 
    * * @@ -42,6 +44,8 @@ public interface GetDatabaseDdlRequestOrBuilder * *
        * Required. The database whose schema we wish to get.
    +   * Values are of the form
    +   * `projects/<project>/instances/<instance>/databases/<database>`
        * 
    * * diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupOperationsRequest.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupOperationsRequest.java index baea7c8f1ff..a49ee408beb 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupOperationsRequest.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupOperationsRequest.java @@ -218,9 +218,9 @@ public com.google.protobuf.ByteString getParentBytes() { * * `done:true` - The operation is complete. * * `metadata.database:prod` - The database the backup was taken from has * a name containing the string "prod". - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` <br/> - * `(metadata.name:howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` \ + * `(metadata.name:howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Returns operations where: * * The operation's metadata type is [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata]. * * The backup name contains the string "howl". @@ -272,9 +272,9 @@ public java.lang.String getFilter() { * * `done:true` - The operation is complete. * * `metadata.database:prod` - The database the backup was taken from has * a name containing the string "prod". - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` <br/> - * `(metadata.name:howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` \ + * `(metadata.name:howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Returns operations where: * * The operation's metadata type is [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata]. * * The backup name contains the string "howl". @@ -894,9 +894,9 @@ public Builder setParentBytes(com.google.protobuf.ByteString value) { * * `done:true` - The operation is complete. * * `metadata.database:prod` - The database the backup was taken from has * a name containing the string "prod". - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` <br/> - * `(metadata.name:howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` \ + * `(metadata.name:howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Returns operations where: * * The operation's metadata type is [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata]. * * The backup name contains the string "howl". @@ -947,9 +947,9 @@ public java.lang.String getFilter() { * * `done:true` - The operation is complete. * * `metadata.database:prod` - The database the backup was taken from has * a name containing the string "prod". - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` <br/> - * `(metadata.name:howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` \ + * `(metadata.name:howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Returns operations where: * * The operation's metadata type is [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata]. * * The backup name contains the string "howl". @@ -1000,9 +1000,9 @@ public com.google.protobuf.ByteString getFilterBytes() { * * `done:true` - The operation is complete. * * `metadata.database:prod` - The database the backup was taken from has * a name containing the string "prod". - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` <br/> - * `(metadata.name:howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` \ + * `(metadata.name:howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Returns operations where: * * The operation's metadata type is [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata]. * * The backup name contains the string "howl". @@ -1052,9 +1052,9 @@ public Builder setFilter(java.lang.String value) { * * `done:true` - The operation is complete. * * `metadata.database:prod` - The database the backup was taken from has * a name containing the string "prod". - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` <br/> - * `(metadata.name:howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` \ + * `(metadata.name:howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Returns operations where: * * The operation's metadata type is [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata]. * * The backup name contains the string "howl". @@ -1100,9 +1100,9 @@ public Builder clearFilter() { * * `done:true` - The operation is complete. * * `metadata.database:prod` - The database the backup was taken from has * a name containing the string "prod". - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` <br/> - * `(metadata.name:howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` \ + * `(metadata.name:howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Returns operations where: * * The operation's metadata type is [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata]. * * The backup name contains the string "howl". diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupOperationsRequestOrBuilder.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupOperationsRequestOrBuilder.java index 4f43eba2884..6d48d7f5f3f 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupOperationsRequestOrBuilder.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupOperationsRequestOrBuilder.java @@ -82,9 +82,9 @@ public interface ListBackupOperationsRequestOrBuilder * * `done:true` - The operation is complete. * * `metadata.database:prod` - The database the backup was taken from has * a name containing the string "prod". - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` <br/> - * `(metadata.name:howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` \ + * `(metadata.name:howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Returns operations where: * * The operation's metadata type is [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata]. * * The backup name contains the string "howl". @@ -125,9 +125,9 @@ public interface ListBackupOperationsRequestOrBuilder * * `done:true` - The operation is complete. * * `metadata.database:prod` - The database the backup was taken from has * a name containing the string "prod". - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` <br/> - * `(metadata.name:howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` \ + * `(metadata.name:howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Returns operations where: * * The operation's metadata type is [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata]. * * The backup name contains the string "howl". diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabaseOperationsRequest.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabaseOperationsRequest.java index 06cff39b688..d59caa1f7de 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabaseOperationsRequest.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabaseOperationsRequest.java @@ -216,11 +216,11 @@ public com.google.protobuf.ByteString getParentBytes() { * you can specify AND, OR, and NOT logic explicitly. * Here are a few examples: * * `done:true` - The operation is complete. - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` <br/> - * `(metadata.source_type:BACKUP) AND` <br/> - * `(metadata.backup_info.backup:backup_howl) AND` <br/> - * `(metadata.name:restored_howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` \ + * `(metadata.source_type:BACKUP) AND` \ + * `(metadata.backup_info.backup:backup_howl) AND` \ + * `(metadata.name:restored_howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Return operations where: * * The operation's metadata type is [RestoreDatabaseMetadata][google.spanner.admin.database.v1.RestoreDatabaseMetadata]. * * The database is restored from a backup. @@ -272,11 +272,11 @@ public java.lang.String getFilter() { * you can specify AND, OR, and NOT logic explicitly. * Here are a few examples: * * `done:true` - The operation is complete. - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` <br/> - * `(metadata.source_type:BACKUP) AND` <br/> - * `(metadata.backup_info.backup:backup_howl) AND` <br/> - * `(metadata.name:restored_howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` \ + * `(metadata.source_type:BACKUP) AND` \ + * `(metadata.backup_info.backup:backup_howl) AND` \ + * `(metadata.name:restored_howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Return operations where: * * The operation's metadata type is [RestoreDatabaseMetadata][google.spanner.admin.database.v1.RestoreDatabaseMetadata]. * * The database is restored from a backup. @@ -900,11 +900,11 @@ public Builder setParentBytes(com.google.protobuf.ByteString value) { * you can specify AND, OR, and NOT logic explicitly. * Here are a few examples: * * `done:true` - The operation is complete. - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` <br/> - * `(metadata.source_type:BACKUP) AND` <br/> - * `(metadata.backup_info.backup:backup_howl) AND` <br/> - * `(metadata.name:restored_howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` \ + * `(metadata.source_type:BACKUP) AND` \ + * `(metadata.backup_info.backup:backup_howl) AND` \ + * `(metadata.name:restored_howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Return operations where: * * The operation's metadata type is [RestoreDatabaseMetadata][google.spanner.admin.database.v1.RestoreDatabaseMetadata]. * * The database is restored from a backup. @@ -955,11 +955,11 @@ public java.lang.String getFilter() { * you can specify AND, OR, and NOT logic explicitly. * Here are a few examples: * * `done:true` - The operation is complete. - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` <br/> - * `(metadata.source_type:BACKUP) AND` <br/> - * `(metadata.backup_info.backup:backup_howl) AND` <br/> - * `(metadata.name:restored_howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` \ + * `(metadata.source_type:BACKUP) AND` \ + * `(metadata.backup_info.backup:backup_howl) AND` \ + * `(metadata.name:restored_howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Return operations where: * * The operation's metadata type is [RestoreDatabaseMetadata][google.spanner.admin.database.v1.RestoreDatabaseMetadata]. * * The database is restored from a backup. @@ -1010,11 +1010,11 @@ public com.google.protobuf.ByteString getFilterBytes() { * you can specify AND, OR, and NOT logic explicitly. * Here are a few examples: * * `done:true` - The operation is complete. - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` <br/> - * `(metadata.source_type:BACKUP) AND` <br/> - * `(metadata.backup_info.backup:backup_howl) AND` <br/> - * `(metadata.name:restored_howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` \ + * `(metadata.source_type:BACKUP) AND` \ + * `(metadata.backup_info.backup:backup_howl) AND` \ + * `(metadata.name:restored_howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Return operations where: * * The operation's metadata type is [RestoreDatabaseMetadata][google.spanner.admin.database.v1.RestoreDatabaseMetadata]. * * The database is restored from a backup. @@ -1064,11 +1064,11 @@ public Builder setFilter(java.lang.String value) { * you can specify AND, OR, and NOT logic explicitly. * Here are a few examples: * * `done:true` - The operation is complete. - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` <br/> - * `(metadata.source_type:BACKUP) AND` <br/> - * `(metadata.backup_info.backup:backup_howl) AND` <br/> - * `(metadata.name:restored_howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` \ + * `(metadata.source_type:BACKUP) AND` \ + * `(metadata.backup_info.backup:backup_howl) AND` \ + * `(metadata.name:restored_howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Return operations where: * * The operation's metadata type is [RestoreDatabaseMetadata][google.spanner.admin.database.v1.RestoreDatabaseMetadata]. * * The database is restored from a backup. @@ -1114,11 +1114,11 @@ public Builder clearFilter() { * you can specify AND, OR, and NOT logic explicitly. * Here are a few examples: * * `done:true` - The operation is complete. - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` <br/> - * `(metadata.source_type:BACKUP) AND` <br/> - * `(metadata.backup_info.backup:backup_howl) AND` <br/> - * `(metadata.name:restored_howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` \ + * `(metadata.source_type:BACKUP) AND` \ + * `(metadata.backup_info.backup:backup_howl) AND` \ + * `(metadata.name:restored_howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Return operations where: * * The operation's metadata type is [RestoreDatabaseMetadata][google.spanner.admin.database.v1.RestoreDatabaseMetadata]. * * The database is restored from a backup. diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabaseOperationsRequestOrBuilder.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabaseOperationsRequestOrBuilder.java index 115dcd7551a..38eb5693e1a 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabaseOperationsRequestOrBuilder.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabaseOperationsRequestOrBuilder.java @@ -80,11 +80,11 @@ public interface ListDatabaseOperationsRequestOrBuilder * you can specify AND, OR, and NOT logic explicitly. * Here are a few examples: * * `done:true` - The operation is complete. - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` <br/> - * `(metadata.source_type:BACKUP) AND` <br/> - * `(metadata.backup_info.backup:backup_howl) AND` <br/> - * `(metadata.name:restored_howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` \ + * `(metadata.source_type:BACKUP) AND` \ + * `(metadata.backup_info.backup:backup_howl) AND` \ + * `(metadata.name:restored_howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Return operations where: * * The operation's metadata type is [RestoreDatabaseMetadata][google.spanner.admin.database.v1.RestoreDatabaseMetadata]. * * The database is restored from a backup. @@ -125,11 +125,11 @@ public interface ListDatabaseOperationsRequestOrBuilder * you can specify AND, OR, and NOT logic explicitly. * Here are a few examples: * * `done:true` - The operation is complete. - * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` <br/> - * `(metadata.source_type:BACKUP) AND` <br/> - * `(metadata.backup_info.backup:backup_howl) AND` <br/> - * `(metadata.name:restored_howl) AND` <br/> - * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` <br/> + * * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` \ + * `(metadata.source_type:BACKUP) AND` \ + * `(metadata.backup_info.backup:backup_howl) AND` \ + * `(metadata.name:restored_howl) AND` \ + * `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ * `(error:*)` - Return operations where: * * The operation's metadata type is [RestoreDatabaseMetadata][google.spanner.admin.database.v1.RestoreDatabaseMetadata]. * * The database is restored from a backup. diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/OptimizeRestoredDatabaseMetadata.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/OptimizeRestoredDatabaseMetadata.java index 88c49cdc159..6bdf68bc71c 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/OptimizeRestoredDatabaseMetadata.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/OptimizeRestoredDatabaseMetadata.java @@ -141,7 +141,7 @@ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { * Name of the restored database being optimized. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The name. */ @@ -164,7 +164,7 @@ public java.lang.String getName() { * Name of the restored database being optimized. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The bytes for name. */ @@ -595,7 +595,7 @@ public Builder mergeFrom( * Name of the restored database being optimized. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The name. */ @@ -617,7 +617,7 @@ public java.lang.String getName() { * Name of the restored database being optimized. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The bytes for name. */ @@ -639,7 +639,7 @@ public com.google.protobuf.ByteString getNameBytes() { * Name of the restored database being optimized. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @param value The name to set. * @return This builder for chaining. @@ -660,7 +660,7 @@ public Builder setName(java.lang.String value) { * Name of the restored database being optimized. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return This builder for chaining. */ @@ -677,7 +677,7 @@ public Builder clearName() { * Name of the restored database being optimized. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @param value The bytes for name to set. * @return This builder for chaining. diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/OptimizeRestoredDatabaseMetadataOrBuilder.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/OptimizeRestoredDatabaseMetadataOrBuilder.java index a5a211d446b..0606518a011 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/OptimizeRestoredDatabaseMetadataOrBuilder.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/OptimizeRestoredDatabaseMetadataOrBuilder.java @@ -30,7 +30,7 @@ public interface OptimizeRestoredDatabaseMetadataOrBuilder * Name of the restored database being optimized. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The name. */ @@ -42,7 +42,7 @@ public interface OptimizeRestoredDatabaseMetadataOrBuilder * Name of the restored database being optimized. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The bytes for name. */ diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreDatabaseMetadata.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreDatabaseMetadata.java index 4cb99151034..8a849d56711 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreDatabaseMetadata.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreDatabaseMetadata.java @@ -230,7 +230,7 @@ public SourceInfoCase getSourceInfoCase() { * Name of the database being created and restored to. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The name. */ @@ -253,7 +253,7 @@ public java.lang.String getName() { * Name of the database being created and restored to. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The bytes for name. */ @@ -1039,7 +1039,7 @@ public Builder clearSourceInfo() { * Name of the database being created and restored to. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The name. */ @@ -1061,7 +1061,7 @@ public java.lang.String getName() { * Name of the database being created and restored to. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The bytes for name. */ @@ -1083,7 +1083,7 @@ public com.google.protobuf.ByteString getNameBytes() { * Name of the database being created and restored to. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @param value The name to set. * @return This builder for chaining. @@ -1104,7 +1104,7 @@ public Builder setName(java.lang.String value) { * Name of the database being created and restored to. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return This builder for chaining. */ @@ -1121,7 +1121,7 @@ public Builder clearName() { * Name of the database being created and restored to. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @param value The bytes for name to set. * @return This builder for chaining. diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreDatabaseMetadataOrBuilder.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreDatabaseMetadataOrBuilder.java index 8d05e8b6b7c..35ad6d76e9d 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreDatabaseMetadataOrBuilder.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreDatabaseMetadataOrBuilder.java @@ -30,7 +30,7 @@ public interface RestoreDatabaseMetadataOrBuilder * Name of the database being created and restored to. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The name. */ @@ -42,7 +42,7 @@ public interface RestoreDatabaseMetadataOrBuilder * Name of the database being created and restored to. * * - * string name = 1; + * string name = 1 [(.google.api.resource_reference) = { ... } * * @return The bytes for name. */ diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SpannerDatabaseAdminProto.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SpannerDatabaseAdminProto.java index 032a813e29b..7b981be4f45 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SpannerDatabaseAdminProto.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SpannerDatabaseAdminProto.java @@ -120,176 +120,181 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "admin.database.v1.RestoreSourceType\022C\n\013b" + "ackup_info\030\002 \001(\0132,.google.spanner.admin." + "database.v1.BackupInfoH\000B\r\n\013source_info\"" - + "\226\003\n\010Database\022\021\n\004name\030\001 \001(\tB\003\340A\002\022D\n\005state" + + "\375\003\n\010Database\022\021\n\004name\030\001 \001(\tB\003\340A\002\022D\n\005state" + "\030\002 \001(\01620.google.spanner.admin.database.v" + "1.Database.StateB\003\340A\003\0224\n\013create_time\030\003 \001" + "(\0132\032.google.protobuf.TimestampB\003\340A\003\022H\n\014r" + "estore_info\030\004 \001(\0132-.google.spanner.admin" - + ".database.v1.RestoreInfoB\003\340A\003\"M\n\005State\022\025" - + "\n\021STATE_UNSPECIFIED\020\000\022\014\n\010CREATING\020\001\022\t\n\005R" - + "EADY\020\002\022\024\n\020READY_OPTIMIZING\020\003:b\352A_\n\037spann" - + "er.googleapis.com/Database\022\202\323\344\223" - + "\002/\022-/v1/{parent=projects/*/instances/*}/" - + "databases\332A\006parent\022\244\002\n\016CreateDatabase\0227." - + "google.spanner.admin.database.v1.CreateD" - + "atabaseRequest\032\035.google.longrunning.Oper" - + "ation\"\271\001\202\323\344\223\0022\"-/v1/{parent=projects/*/i" - + "nstances/*}/databases:\001*\332A\027parent,create" - + "_statement\312Ad\n)google.spanner.admin.data" - + "base.v1.Database\0227google.spanner.admin.d" - + "atabase.v1.CreateDatabaseMetadata\022\255\001\n\013Ge" - + "tDatabase\0224.google.spanner.admin.databas" - + "e.v1.GetDatabaseRequest\032*.google.spanner" - + ".admin.database.v1.Database\"<\202\323\344\223\002/\022-/v1" - + "/{name=projects/*/instances/*/databases/" - + "*}\332A\004name\022\235\002\n\021UpdateDatabaseDdl\022:.google" - + ".spanner.admin.database.v1.UpdateDatabas" - + "eDdlRequest\032\035.google.longrunning.Operati" - + "on\"\254\001\202\323\344\223\002:25/v1/{database=projects/*/in" - + "stances/*/databases/*}/ddl:\001*\332A\023database" - + ",statements\312AS\n\025google.protobuf.Empty\022:g" - + "oogle.spanner.admin.database.v1.UpdateDa" - + "tabaseDdlMetadata\022\243\001\n\014DropDatabase\0225.goo" - + "gle.spanner.admin.database.v1.DropDataba" - + "seRequest\032\026.google.protobuf.Empty\"D\202\323\344\223\002" - + "3*1/v1/{database=projects/*/instances/*/" - + "databases/*}\332A\010database\022\315\001\n\016GetDatabaseD" - + "dl\0227.google.spanner.admin.database.v1.Ge" - + "tDatabaseDdlRequest\0328.google.spanner.adm" - + "in.database.v1.GetDatabaseDdlResponse\"H\202" - + "\323\344\223\0027\0225/v1/{database=projects/*/instance" - + "s/*/databases/*}/ddl\332A\010database\022\353\001\n\014SetI" - + "amPolicy\022\".google.iam.v1.SetIamPolicyReq" - + "uest\032\025.google.iam.v1.Policy\"\237\001\202\323\344\223\002\206\001\">/" - + "v1/{resource=projects/*/instances/*/data" - + "bases/*}:setIamPolicy:\001*ZA\"/v1" - + "/{resource=projects/*/instances/*/databa" - + "ses/*}:getIamPolicy:\001*ZA\".google.spanner.admi" - + "n.database.v1.ListBackupOperationsRespon" - + "se\"E\202\323\344\223\0026\0224/v1/{parent=projects/*/insta" - + "nces/*}/backupOperations\332A\006parent\032x\312A\026sp" - + "anner.googleapis.com\322A\\https://www.googl" - + "eapis.com/auth/cloud-platform,https://ww" - + "w.googleapis.com/auth/spanner.adminB\332\002\n$" - + "com.google.spanner.admin.database.v1B\031Sp" - + "annerDatabaseAdminProtoP\001ZHgoogle.golang" - + ".org/genproto/googleapis/spanner/admin/d" - + "atabase/v1;database\252\002&Google.Cloud.Spann" - + "er.Admin.Database.V1\312\002&Google\\Cloud\\Span" - + "ner\\Admin\\Database\\V1\352\002+Google::Cloud::S" - + "panner::Admin::Database::V1\352AJ\n\037spanner." - + "googleapis.com/Instance\022\'projects/{proje" - + "ct}/instances/{instance}b\006proto3" + + ".database.v1.RestoreInfoB\003\340A\003\022%\n\030version" + + "_retention_period\030\006 \001(\tB\003\340A\003\022>\n\025earliest" + + "_version_time\030\007 \001(\0132\032.google.protobuf.Ti" + + "mestampB\003\340A\003\"M\n\005State\022\025\n\021STATE_UNSPECIFI" + + "ED\020\000\022\014\n\010CREATING\020\001\022\t\n\005READY\020\002\022\024\n\020READY_O" + + "PTIMIZING\020\003:b\352A_\n\037spanner.googleapis.com" + + "/Database\022\202\323\344\223\002/\022-/v1/{parent=proj" + + "ects/*/instances/*}/databases\332A\006parent\022\244" + + "\002\n\016CreateDatabase\0227.google.spanner.admin" + + ".database.v1.CreateDatabaseRequest\032\035.goo" + + "gle.longrunning.Operation\"\271\001\202\323\344\223\0022\"-/v1/" + + "{parent=projects/*/instances/*}/database" + + "s:\001*\332A\027parent,create_statement\312Ad\n)googl" + + "e.spanner.admin.database.v1.Database\0227go" + + "ogle.spanner.admin.database.v1.CreateDat" + + "abaseMetadata\022\255\001\n\013GetDatabase\0224.google.s" + + "panner.admin.database.v1.GetDatabaseRequ" + + "est\032*.google.spanner.admin.database.v1.D" + + "atabase\"<\202\323\344\223\002/\022-/v1/{name=projects/*/in" + + "stances/*/databases/*}\332A\004name\022\235\002\n\021Update" + + "DatabaseDdl\022:.google.spanner.admin.datab" + + "ase.v1.UpdateDatabaseDdlRequest\032\035.google" + + ".longrunning.Operation\"\254\001\202\323\344\223\002:25/v1/{da" + + "tabase=projects/*/instances/*/databases/" + + "*}/ddl:\001*\332A\023database,statements\312AS\n\025goog" + + "le.protobuf.Empty\022:google.spanner.admin." + + "database.v1.UpdateDatabaseDdlMetadata\022\243\001" + + "\n\014DropDatabase\0225.google.spanner.admin.da" + + "tabase.v1.DropDatabaseRequest\032\026.google.p" + + "rotobuf.Empty\"D\202\323\344\223\0023*1/v1/{database=pro" + + "jects/*/instances/*/databases/*}\332A\010datab" + + "ase\022\315\001\n\016GetDatabaseDdl\0227.google.spanner." + + "admin.database.v1.GetDatabaseDdlRequest\032" + + "8.google.spanner.admin.database.v1.GetDa" + + "tabaseDdlResponse\"H\202\323\344\223\0027\0225/v1/{database" + + "=projects/*/instances/*/databases/*}/ddl" + + "\332A\010database\022\353\001\n\014SetIamPolicy\022\".google.ia" + + "m.v1.SetIamPolicyRequest\032\025.google.iam.v1" + + ".Policy\"\237\001\202\323\344\223\002\206\001\">/v1/{resource=project" + + "s/*/instances/*/databases/*}:setIamPolic" + + "y:\001*ZA\"/v1/{resource=projects/" + + "*/instances/*/databases/*}:getIamPolicy:" + + "\001*ZA\"" + + ".google.spanner.admin.database.v1.ListBa" + + "ckupOperationsResponse\"E\202\323\344\223\0026\0224/v1/{par" + + "ent=projects/*/instances/*}/backupOperat" + + "ions\332A\006parent\032x\312A\026spanner.googleapis.com" + + "\322A\\https://www.googleapis.com/auth/cloud" + + "-platform,https://www.googleapis.com/aut" + + "h/spanner.adminB\332\002\n$com.google.spanner.a" + + "dmin.database.v1B\031SpannerDatabaseAdminPr" + + "otoP\001ZHgoogle.golang.org/genproto/google" + + "apis/spanner/admin/database/v1;database\252" + + "\002&Google.Cloud.Spanner.Admin.Database.V1" + + "\312\002&Google\\Cloud\\Spanner\\Admin\\Database\\V" + + "1\352\002+Google::Cloud::Spanner::Admin::Datab" + + "ase::V1\352AJ\n\037spanner.googleapis.com/Insta" + + "nce\022\'projects/{project}/instances/{insta" + + "nce}b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom( @@ -321,7 +326,12 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_google_spanner_admin_database_v1_Database_descriptor, new java.lang.String[] { - "Name", "State", "CreateTime", "RestoreInfo", + "Name", + "State", + "CreateTime", + "RestoreInfo", + "VersionRetentionPeriod", + "EarliestVersionTime", }); internal_static_google_spanner_admin_database_v1_ListDatabasesRequest_descriptor = getDescriptor().getMessageTypes().get(2); diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/backup.proto b/proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/backup.proto index e33faddddf4..a677207f729 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/backup.proto +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/backup.proto @@ -61,6 +61,12 @@ message Backup { type: "spanner.googleapis.com/Database" }]; + // The backup will contain an externally consistent copy of the database at + // the timestamp specified by `version_time`. If `version_time` is not + // specified, the system will set `version_time` to the `create_time` of the + // backup. + google.protobuf.Timestamp version_time = 9; + // Required for the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] // operation. The expiration time of the backup, with microseconds // granularity that must be at least 6 hours and at most 366 days @@ -84,10 +90,9 @@ message Backup { // `projects//instances/`. string name = 1; - // Output only. The backup will contain an externally consistent - // copy of the database at the timestamp specified by - // `create_time`. `create_time` is approximately the time the - // [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request is received. + // Output only. The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] + // request is received. If the request does not specify `version_time`, the + // `version_time` of the backup will be equivalent to the `create_time`. google.protobuf.Timestamp create_time = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; // Output only. Size of the backup in bytes. @@ -134,10 +139,14 @@ message CreateBackupRequest { // [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]. message CreateBackupMetadata { // The name of the backup being created. - string name = 1; + string name = 1 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/Backup" + }]; // The name of the database the backup is created from. - string database = 2; + string database = 2 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + }]; // The progress of the // [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] operation. @@ -311,9 +320,9 @@ message ListBackupOperationsRequest { // * `done:true` - The operation is complete. // * `metadata.database:prod` - The database the backup was taken from has // a name containing the string "prod". - // * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND`
    - // `(metadata.name:howl) AND`
    - // `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND`
    + // * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` \ + // `(metadata.name:howl) AND` \ + // `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ // `(error:*)` - Returns operations where: // * The operation's metadata type is [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata]. // * The backup name contains the string "howl". @@ -355,12 +364,23 @@ message ListBackupOperationsResponse { // Information about a backup. message BackupInfo { // Name of the backup. - string backup = 1; + string backup = 1 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/Backup" + }]; // The backup contains an externally consistent copy of `source_database` at - // the timestamp specified by `create_time`. + // the timestamp specified by `version_time`. If the + // [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request did not specify + // `version_time`, the `version_time` of the backup is equivalent to the + // `create_time`. + google.protobuf.Timestamp version_time = 4; + + // The time the [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] request was + // received. google.protobuf.Timestamp create_time = 2; // Name of the database the backup was created from. - string source_database = 3; + string source_database = 3 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + }]; } diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/spanner_database_admin.proto b/proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/spanner_database_admin.proto index db6192bc026..12e751bd672 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/spanner_database_admin.proto +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/spanner_database_admin.proto @@ -368,6 +368,17 @@ message Database { // Output only. Applicable only for restored databases. Contains information // about the restore source. RestoreInfo restore_info = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The period in which Cloud Spanner retains all versions of data + // for the database. This is the same as the value of version_retention_period + // database option set using + // [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl]. Defaults to 1 hour, + // if not set. + string version_retention_period = 6 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. Earliest timestamp at which older versions of the data can be + // read. + google.protobuf.Timestamp earliest_version_time = 7 [(google.api.field_behavior) = OUTPUT_ONLY]; } // The request for [ListDatabases][google.spanner.admin.database.v1.DatabaseAdmin.ListDatabases]. @@ -535,6 +546,8 @@ message DropDatabaseRequest { // The request for [GetDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.GetDatabaseDdl]. message GetDatabaseDdlRequest { // Required. The database whose schema we wish to get. + // Values are of the form + // `projects//instances//databases/` string database = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = { @@ -590,11 +603,11 @@ message ListDatabaseOperationsRequest { // Here are a few examples: // // * `done:true` - The operation is complete. - // * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND`
    - // `(metadata.source_type:BACKUP) AND`
    - // `(metadata.backup_info.backup:backup_howl) AND`
    - // `(metadata.name:restored_howl) AND`
    - // `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND`
    + // * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` \ + // `(metadata.source_type:BACKUP) AND` \ + // `(metadata.backup_info.backup:backup_howl) AND` \ + // `(metadata.name:restored_howl) AND` \ + // `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ // `(error:*)` - Return operations where: // * The operation's metadata type is [RestoreDatabaseMetadata][google.spanner.admin.database.v1.RestoreDatabaseMetadata]. // * The database is restored from a backup. @@ -666,7 +679,9 @@ message RestoreDatabaseRequest { // [RestoreDatabase][google.spanner.admin.database.v1.DatabaseAdmin.RestoreDatabase]. message RestoreDatabaseMetadata { // Name of the database being created and restored to. - string name = 1; + string name = 1 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + }]; // The type of the restore source. RestoreSourceType source_type = 2; @@ -716,7 +731,9 @@ message RestoreDatabaseMetadata { // completion of a database restore, and cannot be cancelled. message OptimizeRestoredDatabaseMetadata { // Name of the restored database being optimized. - string name = 1; + string name = 1 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + }]; // The progress of the post-restore optimizations. OperationProgress progress = 2; diff --git a/synth.metadata b/synth.metadata index d9eb2060851..383b16ed68b 100644 --- a/synth.metadata +++ b/synth.metadata @@ -11,8 +11,8 @@ "git": { "name": "googleapis", "remote": "https://github.com/googleapis/googleapis.git", - "sha": "8fa381b7138f1d72966ff20563efae1b2194d359", - "internalRef": "353145174" + "sha": "30eb779ddcc4f389df34d0802d081ef6c4559d6d", + "internalRef": "354462069" } }, { From 61ea2b940cadab8952900b3465ac5bf24f1c47f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Mon, 1 Feb 2021 11:06:03 +0100 Subject: [PATCH 19/42] test: reduce wait time for BackendExhaustedTest (#835) The BackendExhaustedTest deliberately causes requests to get stuck on the mock server. This causes the Spanner instance to refuse to shutdown nicely and causes it to wait for 10 seconds. This can be circumvented by using shutdownNow() to force the closure of all gRPC transport channels. This reduces the overall build time with approx 10 seconds. --- .../com/google/cloud/spanner/SpannerImpl.java | 7 ++++++- .../cloud/spanner/spi/v1/GapicSpannerRpc.java | 16 ++++++++++++++++ .../cloud/spanner/BackendExhaustedTest.java | 3 +-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java index 2d034eda883..9fa8b30be28 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java @@ -25,6 +25,7 @@ import com.google.cloud.grpc.GrpcTransportOptions; import com.google.cloud.spanner.SessionClient.SessionId; import com.google.cloud.spanner.SpannerOptions.CloseableExecutorProvider; +import com.google.cloud.spanner.spi.v1.GapicSpannerRpc; import com.google.cloud.spanner.spi.v1.SpannerRpc; import com.google.cloud.spanner.spi.v1.SpannerRpc.Paginated; import com.google.common.annotations.VisibleForTesting; @@ -269,7 +270,11 @@ void close(long timeout, TimeUnit unit) { sessionClients.clear(); asyncExecutorProvider.close(); try { - gapicRpc.shutdown(); + if (timeout == Long.MAX_VALUE || !(gapicRpc instanceof GapicSpannerRpc)) { + gapicRpc.shutdown(); + } else { + ((GapicSpannerRpc) gapicRpc).shutdownNow(); + } } catch (RuntimeException e) { logger.log(Level.WARNING, "Failed to close channels", e); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java index 5172fe448fb..807eac6f11b 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java @@ -1718,6 +1718,22 @@ public void shutdown() { } } + public void shutdownNow() { + this.rpcIsClosed = true; + this.spannerStub.close(); + this.partitionedDmlStub.close(); + this.instanceAdminStub.close(); + this.databaseAdminStub.close(); + this.spannerWatchdog.shutdown(); + this.executorProvider.shutdown(); + + this.spannerStub.shutdownNow(); + this.partitionedDmlStub.shutdownNow(); + this.instanceAdminStub.shutdownNow(); + this.databaseAdminStub.shutdownNow(); + this.spannerWatchdog.shutdownNow(); + } + @Override public boolean isClosed() { return rpcIsClosed; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BackendExhaustedTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BackendExhaustedTest.java index cb1244f8e3e..7e4e41facc0 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BackendExhaustedTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BackendExhaustedTest.java @@ -136,7 +136,6 @@ public void setUp() throws Exception { SessionPoolOptions.newBuilder() .setMinSessions(executor.getCorePoolSize()) .setMaxSessions(executor.getCorePoolSize() * 3) - .setWriteSessionsFraction(0.0f) .build()) .build(); executorFactory.release(executor); @@ -159,7 +158,7 @@ public void tearDown() { // This test case force-closes the Spanner instance as it would otherwise wait // forever on the BatchCreateSessions requests that are 'stuck'. try { - ((SpannerImpl) spanner).close(100L, TimeUnit.MILLISECONDS); + ((SpannerImpl) spanner).close(10L, TimeUnit.MILLISECONDS); } catch (SpannerException e) { // ignore any errors during close as they are expected. } From 1c4eead835c455b9bb04dc3d0aab7a230375760e Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Mon, 1 Feb 2021 19:02:15 -0800 Subject: [PATCH 20/42] build: migrate to flakybot (#839) Source-Author: Justin Beckwith Source-Date: Thu Jan 28 22:22:38 2021 -0800 Source-Repo: googleapis/synthtool Source-Sha: d1bb9173100f62c0cfc8f3138b62241e7f47ca6a Source-Link: https://github.com/googleapis/synthtool/commit/d1bb9173100f62c0cfc8f3138b62241e7f47ca6a --- .kokoro/build.sh | 4 ++-- synth.metadata | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.kokoro/build.sh b/.kokoro/build.sh index 8280d10ad67..4572da72d57 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -110,8 +110,8 @@ bash .kokoro/coerce_logs.sh if [[ "${ENABLE_BUILD_COP}" == "true" ]] then - chmod +x ${KOKORO_GFILE_DIR}/linux_amd64/buildcop - ${KOKORO_GFILE_DIR}/linux_amd64/buildcop -repo=googleapis/java-spanner + chmod +x ${KOKORO_GFILE_DIR}/linux_amd64/flakybot + ${KOKORO_GFILE_DIR}/linux_amd64/flakybot -repo=googleapis/java-spanner fi echo "exiting with ${RETURN_CODE}" diff --git a/synth.metadata b/synth.metadata index 383b16ed68b..3ab9fe79bde 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "facda8a7d07a9cf08b1847678b24b270f1abdb38" + "sha": "004c5d759804a380e65d1023faef0978de5d3ff9" } }, { @@ -19,7 +19,7 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "3816b080296d4d52975079fd26c110dd26ba25af" + "sha": "d1bb9173100f62c0cfc8f3138b62241e7f47ca6a" } } ], From 77b7cfcd1b2cc4825e226f219900f8eab29732df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Tue, 2 Feb 2021 04:24:24 +0100 Subject: [PATCH 21/42] test: suppress logging a known warning (#841) --- .../cloud/spanner/connection/SpannerPoolTest.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java index 19d6dfddcde..9174ef81b22 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java @@ -169,6 +169,7 @@ public void testRemoveConnection() { private static Logger log = Logger.getLogger(SpannerPool.class.getName()); private static OutputStream logCapturingStream; private static StreamHandler customLogHandler; + private static boolean useParentHandlers; private void attachLogCapturer() { logCapturingStream = new ByteArrayOutputStream(); @@ -182,14 +183,23 @@ private void attachLogCapturer() { throw new IllegalStateException("no handlers found for logger"); } customLogHandler = new StreamHandler(logCapturingStream, handlers[0].getFormatter()); + useParentHandlers = log.getUseParentHandlers(); + log.setUseParentHandlers(false); log.addHandler(customLogHandler); } - public String getTestCapturedLog() { + private String getTestCapturedLog() { customLogHandler.flush(); return logCapturingStream.toString(); } + @AfterClass + public static void resetUseParentHandlers() { + if (useParentHandlers) { + log.setUseParentHandlers(true); + } + } + @Test public void testRemoveConnectionOptionsNotRegistered() { attachLogCapturer(); From 118d1b31f5f7771023766fd72a8229db80f1f5a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Tue, 2 Feb 2021 04:28:36 +0100 Subject: [PATCH 22/42] fix: make compiled statements immutable (#843) --- .../ClientSideStatementExecutor.java | 13 ------------- .../connection/ClientSideStatementImpl.java | 11 +++++++---- .../ClientSideStatementNoParamExecutor.java | 14 +++++++++----- .../ClientSideStatementSetExecutor.java | 18 ++++++++++++------ 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementExecutor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementExecutor.java index 24681939187..7ec09766697 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementExecutor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementExecutor.java @@ -16,8 +16,6 @@ package com.google.cloud.spanner.connection; -import com.google.cloud.spanner.connection.ClientSideStatementImpl.CompileException; - /** * A {@link ClientSideStatementExecutor} is used to compile {@link ClientSideStatement}s from the * json source file, and to execute these against a {@link Connection} (through a {@link @@ -25,17 +23,6 @@ */ interface ClientSideStatementExecutor { - /** - * Compiles the given {@link ClientSideStatementImpl} and registers this statement with this - * executor. A statement must be compiled before it can be executed. The parser automatically - * compiles all available statements during initialization. - * - * @param statement The statement to compile. - * @throws CompileException If the statement could not be compiled. This should never happen, as - * it would indicate that an invalid statement has been defined in the source file. - */ - void compile(ClientSideStatementImpl statement) throws CompileException; - /** * Executes the {@link ClientSideStatementImpl} that has been compiled and registered with this * executor on the specified connection. diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementImpl.java index 7621a0ced5c..e9c9c1654e9 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementImpl.java @@ -19,6 +19,7 @@ import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.connection.StatementResult.ResultType; import com.google.common.base.Preconditions; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.List; @@ -143,10 +144,12 @@ public String getMessage() { ClientSideStatementImpl compile() throws CompileException { try { this.pattern = Pattern.compile(regex); - this.executor = - (ClientSideStatementExecutor) - Class.forName(getClass().getPackage().getName() + "." + executorName).newInstance(); - this.executor.compile(this); + @SuppressWarnings("unchecked") + Constructor constructor = + (Constructor) + Class.forName(getClass().getPackage().getName() + "." + executorName) + .getDeclaredConstructor(ClientSideStatementImpl.class); + this.executor = constructor.newInstance(this); return this; } catch (Exception e) { throw new CompileException(e, this); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementNoParamExecutor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementNoParamExecutor.java index 4c31e3e28d2..6f5e683ce64 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementNoParamExecutor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementNoParamExecutor.java @@ -24,12 +24,16 @@ * SHOW AUTOCOMMIT. The executor just calls a method with no parameters. */ class ClientSideStatementNoParamExecutor implements ClientSideStatementExecutor { - private Method method; + private final Method method; - ClientSideStatementNoParamExecutor() {} - - @Override - public void compile(ClientSideStatementImpl statement) throws CompileException { + /** + * Creates and compiles the given {@link ClientSideStatementImpl}. + * + * @param statement The statement to compile. + * @throws CompileException If the statement could not be compiled. This should never happen, as + * it would indicate that an invalid statement has been defined in the source file. + */ + ClientSideStatementNoParamExecutor(ClientSideStatementImpl statement) throws CompileException { try { this.method = ConnectionStatementExecutor.class.getDeclaredMethod(statement.getMethodName()); } catch (NoSuchMethodException | SecurityException e) { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementSetExecutor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementSetExecutor.java index 54765282f5c..4f4fe140428 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementSetExecutor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementSetExecutor.java @@ -30,14 +30,20 @@ * AUTOCOMMIT=TRUE. */ class ClientSideStatementSetExecutor implements ClientSideStatementExecutor { - private ClientSideStatementImpl statement; - private Method method; - private ClientSideStatementValueConverter converter; - private Pattern allowedValuesPattern; + private final ClientSideStatementImpl statement; + private final Method method; + private final ClientSideStatementValueConverter converter; + private final Pattern allowedValuesPattern; + /** + * Creates and compiles the given {@link ClientSideStatementImpl}. + * + * @param statement The statement to compile. + * @throws CompileException If the statement could not be compiled. This should never happen, as + * it would indicate that an invalid statement has been defined in the source file. + */ @SuppressWarnings("unchecked") - @Override - public void compile(ClientSideStatementImpl statement) throws CompileException { + ClientSideStatementSetExecutor(ClientSideStatementImpl statement) throws CompileException { Preconditions.checkNotNull(statement.getSetStatement()); try { this.statement = statement; From 2c745f31288d5f6ad501b5542ea01db72e015d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Tue, 2 Feb 2021 04:29:16 +0100 Subject: [PATCH 23/42] cleanup: return instead of fallthrough + improve comment (#844) Return the TransactionSelector that is built instead of falling through to the end of the method. The behavior does not change by this, but it makes it easier to read and understand the code. Fixes #830 --- .../java/com/google/cloud/spanner/TransactionRunnerImpl.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java index c3973c1807d..a4973c29345 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java @@ -463,7 +463,10 @@ TransactionSelector getTransactionSelector() { // Aborted error if the call that included the BeginTransaction option fails. The // Aborted error will cause the entire transaction to be retried, and the retry will use // a separate BeginTransaction RPC. - TransactionSelector.newBuilder() + // If tx.get() returns successfully, this.transactionId will also have been set to a + // valid value as the latter is always set when a transaction id is returned by a + // statement. + return TransactionSelector.newBuilder() .setId(tx.get(waitForTransactionTimeoutMillis, TimeUnit.MILLISECONDS)) .build(); } From f9ac29cd05e775c8ea17e0e227eb925df88a8dba Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Tue, 2 Feb 2021 14:12:03 -0800 Subject: [PATCH 24/42] build(java): generate docfx yml on release (#845) This PR was generated using Autosynth. :rainbow: Synth log will be available here: https://source.cloud.google.com/results/invocations/6670e800-5ae0-42a0-9f59-45848d8d694a/targets - [ ] To automatically regenerate this PR, check this box. Source-Link: https://github.com/googleapis/synthtool/commit/5de29e9434b63ea6d7e46dc348521c62969af1a1 --- .kokoro/release/publish_javadoc.cfg | 5 +-- .kokoro/release/publish_javadoc11.cfg | 30 +++++++++++++++ .kokoro/release/publish_javadoc11.sh | 55 +++++++++++++++++++++++++++ synth.metadata | 6 ++- 4 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 .kokoro/release/publish_javadoc11.cfg create mode 100755 .kokoro/release/publish_javadoc11.sh diff --git a/.kokoro/release/publish_javadoc.cfg b/.kokoro/release/publish_javadoc.cfg index e41d593e948..cbde2338f3d 100644 --- a/.kokoro/release/publish_javadoc.cfg +++ b/.kokoro/release/publish_javadoc.cfg @@ -26,7 +26,4 @@ before_action { keyname: "docuploader_service_account" } } -} - -# Downloads docfx doclet resource. This will be in ${KOKORO_GFILE_DIR}/ -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/docfx" \ No newline at end of file +} \ No newline at end of file diff --git a/.kokoro/release/publish_javadoc11.cfg b/.kokoro/release/publish_javadoc11.cfg new file mode 100644 index 00000000000..2ddd71ce63e --- /dev/null +++ b/.kokoro/release/publish_javadoc11.cfg @@ -0,0 +1,30 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "STAGING_BUCKET_V2" + value: "docs-staging-v2" + # Production will be at: docs-staging-v2 +} + +# Configure the docker image for kokoro-trampoline +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/java11" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/java-memcache/.kokoro/release/publish_javadoc11.sh" +} + +before_action { + fetch_keystore { + keystore_resource { + keystore_config_id: 73713 + keyname: "docuploader_service_account" + } + } +} + +# Downloads docfx doclet resource. This will be in ${KOKORO_GFILE_DIR}/ +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/docfx" \ No newline at end of file diff --git a/.kokoro/release/publish_javadoc11.sh b/.kokoro/release/publish_javadoc11.sh new file mode 100755 index 00000000000..63af49cf0da --- /dev/null +++ b/.kokoro/release/publish_javadoc11.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# Copyright 2021 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -eo pipefail + +if [[ -z "${CREDENTIALS}" ]]; then + CREDENTIALS=${KOKORO_KEYSTORE_DIR}/73713_docuploader_service_account +fi + +if [[ -z "${STAGING_BUCKET_V2}" ]]; then + echo "Need to set STAGING_BUCKET_V2 environment variable" + exit 1 +fi + +# work from the git root directory +pushd $(dirname "$0")/../../ + +# install docuploader package +python3 -m pip install gcp-docuploader + +# compile all packages +mvn clean install -B -q -DskipTests=true + +export NAME=google-cloud-spanner +export VERSION=$(grep ${NAME}: versions.txt | cut -d: -f3) + +# V3 generates docfx yml from javadoc +# generate yml +mvn clean site -B -q -P docFX + +pushd target/docfx-yml + +# create metadata +python3 -m docuploader create-metadata \ + --name ${NAME} \ + --version ${VERSION} \ + --language java + +# upload yml +python3 -m docuploader upload . \ + --credentials ${CREDENTIALS} \ + --staging-bucket ${STAGING_BUCKET_V2} \ + --destination-prefix docfx- diff --git a/synth.metadata b/synth.metadata index 3ab9fe79bde..7d46ad4dc0b 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "004c5d759804a380e65d1023faef0978de5d3ff9" + "sha": "2c745f31288d5f6ad501b5542ea01db72e015d1e" } }, { @@ -19,7 +19,7 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "d1bb9173100f62c0cfc8f3138b62241e7f47ca6a" + "sha": "5de29e9434b63ea6d7e46dc348521c62969af1a1" } } ], @@ -102,6 +102,8 @@ ".kokoro/release/promote.sh", ".kokoro/release/publish_javadoc.cfg", ".kokoro/release/publish_javadoc.sh", + ".kokoro/release/publish_javadoc11.cfg", + ".kokoro/release/publish_javadoc11.sh", ".kokoro/release/snapshot.cfg", ".kokoro/release/snapshot.sh", ".kokoro/release/stage.cfg", From 2887bbb77141ddc92d8ef79d6221a041b4330999 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Thu, 4 Feb 2021 12:54:10 -0800 Subject: [PATCH 25/42] build(java): run linkage monitor as GitHub action (#846) This PR was generated using Autosynth. :rainbow: Synth log will be available here: https://source.cloud.google.com/results/invocations/5d05a436-b3b3-491d-bbf9-6b053be3c812/targets - [ ] To automatically regenerate this PR, check this box. Source-Link: https://github.com/googleapis/synthtool/commit/e935c9ecb47da0f2e054f5f1845f7cf7c95fa625 --- .github/workflows/ci.yaml | 13 ++++++++++- .kokoro/linkage-monitor.sh | 46 -------------------------------------- synth.metadata | 5 ++--- 3 files changed, 14 insertions(+), 50 deletions(-) delete mode 100755 .kokoro/linkage-monitor.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 68302207566..98a472e132d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -54,7 +54,18 @@ jobs: with: java-version: 8 - run: java -version - - run: .kokoro/linkage-monitor.sh + - name: Install artifacts to local Maven repository + run: | + source .kokoro/common.sh + retry_with_backoff 3 10 \ + mvn install -B -V \ + -Dmaven.test.skip -DskipTests=true \ + -Dclirr.skip=true \ + -Denforcer.skip=true \ + -Dmaven.javadoc.skip=true \ + -Dgcloud.download.skip=true + shell: bash + - uses: GoogleCloudPlatform/cloud-opensource-java/linkage-monitor@v1-linkagemonitor lint: runs-on: ubuntu-latest steps: diff --git a/.kokoro/linkage-monitor.sh b/.kokoro/linkage-monitor.sh deleted file mode 100755 index 759ab4e2c22..00000000000 --- a/.kokoro/linkage-monitor.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -eo pipefail -# Display commands being run. -set -x - -## Get the directory of the build script -scriptDir=$(realpath $(dirname "${BASH_SOURCE[0]}")) -## cd to the parent directory, i.e. the root of the git repo -cd ${scriptDir}/.. - -# include common functions -source ${scriptDir}/common.sh - -# Print out Java version -java -version -echo ${JOB_TYPE} - -# attempt to install 3 times with exponential backoff (starting with 10 seconds) -retry_with_backoff 3 10 \ - mvn install -B -V \ - -DskipTests=true \ - -Dclirr.skip=true \ - -Denforcer.skip=true \ - -Dmaven.javadoc.skip=true \ - -Dgcloud.download.skip=true - -# Kokoro job cloud-opensource-java/ubuntu/linkage-monitor-gcs creates this JAR -JAR=linkage-monitor-latest-all-deps.jar -curl -v -O "https://storage.googleapis.com/cloud-opensource-java-linkage-monitor/${JAR}" - -# Fails if there's new linkage errors compared with baseline -java -jar ${JAR} com.google.cloud:libraries-bom diff --git a/synth.metadata b/synth.metadata index 7d46ad4dc0b..a25f31efdaa 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "2c745f31288d5f6ad501b5542ea01db72e015d1e" + "sha": "f9ac29cd05e775c8ea17e0e227eb925df88a8dba" } }, { @@ -19,7 +19,7 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "5de29e9434b63ea6d7e46dc348521c62969af1a1" + "sha": "e935c9ecb47da0f2e054f5f1845f7cf7c95fa625" } } ], @@ -74,7 +74,6 @@ ".kokoro/continuous/java8.cfg", ".kokoro/continuous/readme.cfg", ".kokoro/dependencies.sh", - ".kokoro/linkage-monitor.sh", ".kokoro/nightly/integration.cfg", ".kokoro/nightly/java11.cfg", ".kokoro/nightly/java7.cfg", From 1d5f71cd3c4fce60846bfaa8b055acffbc119810 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Thu, 4 Feb 2021 15:32:22 -0800 Subject: [PATCH 26/42] chore: Re-generated to pick up changes from synthtool. (#851) This PR was generated using Autosynth. :rainbow: Synth log will be available here: https://source.cloud.google.com/results/invocations/b107ca23-cc03-4d8e-82d2-0b35f1361777/targets - [ ] To automatically regenerate this PR, check this box. Source-Link: https://github.com/googleapis/synthtool/commit/692715c0f23a7bb3bfbbaa300f7620ddfa8c47e5 Source-Link: https://github.com/googleapis/synthtool/commit/27b2d4f4674840628d0b75c5941e89c12af4764f Source-Link: https://github.com/googleapis/synthtool/commit/140ba24a136c63e7f10a998a63e7898aed63ea7d --- .github/workflows/auto-release.yaml | 2 +- .kokoro/release/publish_javadoc11.cfg | 4 ++-- LICENSE | 1 - synth.metadata | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/auto-release.yaml b/.github/workflows/auto-release.yaml index 2b6cdbc976c..76e6404b4e0 100644 --- a/.github/workflows/auto-release.yaml +++ b/.github/workflows/auto-release.yaml @@ -17,7 +17,7 @@ jobs: } // only approve PRs like "chore: release " - if ( !context.payload.pull_request.title.startsWith("chore: release") ) { + if ( !context.payload.pull_request.title.startsWith("chore(master): release") ) { return; } diff --git a/.kokoro/release/publish_javadoc11.cfg b/.kokoro/release/publish_javadoc11.cfg index 2ddd71ce63e..02cd9b1ccff 100644 --- a/.kokoro/release/publish_javadoc11.cfg +++ b/.kokoro/release/publish_javadoc11.cfg @@ -14,7 +14,7 @@ env_vars: { env_vars: { key: "TRAMPOLINE_BUILD_FILE" - value: "github/java-memcache/.kokoro/release/publish_javadoc11.sh" + value: "github/java-spanner/.kokoro/release/publish_javadoc11.sh" } before_action { @@ -27,4 +27,4 @@ before_action { } # Downloads docfx doclet resource. This will be in ${KOKORO_GFILE_DIR}/ -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/docfx" \ No newline at end of file +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/docfx" diff --git a/LICENSE b/LICENSE index d6456956733..261eeb9e9f8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,3 @@ - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/synth.metadata b/synth.metadata index a25f31efdaa..7e28947f74e 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "f9ac29cd05e775c8ea17e0e227eb925df88a8dba" + "sha": "2887bbb77141ddc92d8ef79d6221a041b4330999" } }, { @@ -19,7 +19,7 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "e935c9ecb47da0f2e054f5f1845f7cf7c95fa625" + "sha": "692715c0f23a7bb3bfbbaa300f7620ddfa8c47e5" } } ], From 58cebd85a9d82bd1526b9eae98892181f1a022f1 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Mon, 8 Feb 2021 03:12:52 +0100 Subject: [PATCH 27/42] deps: update dependency com.google.cloud:google-cloud-monitoring to v2.0.12 (#854) --- samples/install-without-bom/pom.xml | 2 +- samples/snapshot/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index 8658f8208b0..5373c065550 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -23,7 +23,7 @@ UTF-8 0.28.3 1.2.11 - 2.0.11 + 2.0.12
    diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 93f5eff505b..e66974d9002 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -23,7 +23,7 @@ UTF-8 0.28.3 1.2.11 - 2.0.11 + 2.0.12 From c451f86af5dade8653356bc30436199db9328061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Tue, 9 Feb 2021 02:14:06 +0100 Subject: [PATCH 28/42] cleanup: remove unused transaction manager (#858) The SingleUseTransaction in the Connection API previously used a TransactionManager instead of a TransactionRunner, as this was the only way that it was possible to implement a statement-specific timeout. This is now supported by the client library, and the SingleUseTransaction had already been moved to using only a TransactionRunner. There was however still an unused TransactionManager reference in the code. Fixes #852 --- .../spanner/connection/SingleUseTransaction.java | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java index 52011eb9100..edabb7f0177 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java @@ -33,7 +33,6 @@ import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.TimestampBound; import com.google.cloud.spanner.TransactionContext; -import com.google.cloud.spanner.TransactionManager; import com.google.cloud.spanner.TransactionRunner; import com.google.cloud.spanner.TransactionRunner.TransactionCallable; import com.google.cloud.spanner.connection.StatementParser.ParsedStatement; @@ -71,7 +70,6 @@ class SingleUseTransaction extends AbstractBaseUnitOfWork { private final TimestampBound readOnlyStaleness; private final AutocommitDmlMode autocommitDmlMode; private volatile SettableApiFuture readTimestamp = null; - private volatile TransactionManager txManager; private volatile TransactionRunner writeTransaction; private boolean used = false; private volatile UnitOfWorkState state = UnitOfWorkState.STARTED; @@ -221,29 +219,21 @@ public Timestamp getReadTimestampOrNull() { } private boolean hasCommitTimestamp() { - return state == UnitOfWorkState.COMMITTED - && (writeTransaction != null - || (txManager != null - && txManager.getState() - == com.google.cloud.spanner.TransactionManager.TransactionState.COMMITTED)); + return state == UnitOfWorkState.COMMITTED && writeTransaction != null; } @Override public Timestamp getCommitTimestamp() { ConnectionPreconditions.checkState( hasCommitTimestamp(), "There is no commit timestamp available for this transaction."); - return writeTransaction != null - ? writeTransaction.getCommitTimestamp() - : txManager.getCommitTimestamp(); + return writeTransaction.getCommitTimestamp(); } @Override public Timestamp getCommitTimestampOrNull() { if (hasCommitTimestamp()) { try { - return writeTransaction != null - ? writeTransaction.getCommitTimestamp() - : txManager.getCommitTimestamp(); + return writeTransaction.getCommitTimestamp(); } catch (SpannerException e) { // ignore } From c86fb5e24459f4d4b3d5c2ceda55c0bbe2cacabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Tue, 9 Feb 2021 02:21:19 +0100 Subject: [PATCH 29/42] test: force-close to speed up timeout tests (#840) * test: force-close to speed up timeout tests * fix: mark connection as closed before waiting * fix: set closed before shutting down * cleanup: add test cases + remove unused code --- .../spanner/connection/ConnectionImpl.java | 12 ++++-- .../cloud/spanner/connection/SpannerPool.java | 34 +++++++++++----- .../spanner/connection/StatementExecutor.java | 8 ++++ .../spanner/ForceCloseSpannerFunction.java | 40 +++++++++++++++++++ .../connection/AbstractMockServerTest.java | 17 ++++++-- .../spanner/connection/SpannerPoolTest.java | 15 ++++--- 6 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/ForceCloseSpannerFunction.java diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java index b49adbf1245..0c91e8060e7 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java @@ -259,11 +259,17 @@ public void close() { // Ignore as we are closing the connection. } } - statementExecutor.shutdownNow(); - spannerPool.removeConnection(options, this); + // Try to wait for the current statement to finish (if any) before we actually close the + // connection. + this.closed = true; + statementExecutor.shutdown(); leakedException = null; + spannerPool.removeConnection(options, this); + statementExecutor.awaitTermination(10L, TimeUnit.SECONDS); + } catch (InterruptedException e) { + // ignore and continue to close the connection. } finally { - this.closed = true; + statementExecutor.shutdownNow(); } } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java index be5b16e393b..51bfde8f00e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java @@ -25,6 +25,7 @@ import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.base.Predicates; @@ -62,6 +63,15 @@ public class SpannerPool { private static final String CONNECTION_API_CLIENT_LIB_TOKEN = "sp-jdbc"; private static final Logger logger = Logger.getLogger(SpannerPool.class.getName()); + private static final Function DEFAULT_CLOSE_FUNCTION = + new Function() { + @Override + public Void apply(Spanner spanner) { + spanner.close(); + return null; + } + }; + /** * Closes the default {@link SpannerPool} and all {@link Spanner} instances that have been opened * by connections and that are still open. Call this method at the end of your application to @@ -395,6 +405,12 @@ void checkAndCloseSpanners() { @VisibleForTesting void checkAndCloseSpanners(CheckAndCloseSpannersMode mode) { + checkAndCloseSpanners(mode, DEFAULT_CLOSE_FUNCTION); + } + + @VisibleForTesting + void checkAndCloseSpanners( + CheckAndCloseSpannersMode mode, Function closeSpannerFunction) { List keysStillInUse = new ArrayList<>(); synchronized (this) { for (Entry entry : spanners.entrySet()) { @@ -416,7 +432,7 @@ void checkAndCloseSpanners(CheckAndCloseSpannersMode mode) { // Force close all Spanner instances by passing in a value that will always be less than // the // difference between the current time and the close time of a connection. - closeUnusedSpanners(Long.MIN_VALUE); + closeUnusedSpanners(Long.MIN_VALUE, closeSpannerFunction); } else { logLeakedConnections(keysStillInUse); throw SpannerExceptionFactory.newSpannerException( @@ -456,6 +472,11 @@ private void logLeakedConnections(List keysStillInUse) { */ @VisibleForTesting void closeUnusedSpanners(long closeSpannerAfterMillisecondsUnused) { + closeUnusedSpanners(closeSpannerAfterMillisecondsUnused, DEFAULT_CLOSE_FUNCTION); + } + + void closeUnusedSpanners( + long closeSpannerAfterMillisecondsUnused, Function closeSpannerFunction) { List keysToBeRemoved = new ArrayList<>(); synchronized (this) { for (Entry entry : lastConnectionClosedAt.entrySet()) { @@ -469,7 +490,9 @@ void closeUnusedSpanners(long closeSpannerAfterMillisecondsUnused) { Spanner spanner = spanners.get(entry.getKey()); if (spanner != null) { try { - spanner.close(); + closeSpannerFunction.apply(spanner); + } catch (Throwable t) { + // Ignore any errors and continue with the next one in the pool. } finally { // Even if the close operation failed, we should remove the spanner object as it is no // longer valid. @@ -484,11 +507,4 @@ void closeUnusedSpanners(long closeSpannerAfterMillisecondsUnused) { } } } - - @VisibleForTesting - int getCurrentSpannerCount() { - synchronized (this) { - return spanners.size(); - } - } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementExecutor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementExecutor.java index baaadbe1676..131533a3374 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementExecutor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementExecutor.java @@ -164,6 +164,14 @@ private static ListeningExecutorService createExecutorService() { this.interceptors = Collections.unmodifiableList(interceptors); } + void shutdown() { + executor.shutdown(); + } + + void awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + executor.awaitTermination(timeout, unit); + } + /** * Shutdown this executor now and do not wait for any statement that is being executed to finish. */ diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ForceCloseSpannerFunction.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ForceCloseSpannerFunction.java new file mode 100644 index 00000000000..a065a20bb52 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ForceCloseSpannerFunction.java @@ -0,0 +1,40 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner; + +import com.google.common.base.Function; +import java.util.concurrent.TimeUnit; + +/** Class for tests that need to be able to force-close a {@link Spanner} instance. */ +public class ForceCloseSpannerFunction implements Function { + private final long timeout; + private final TimeUnit unit; + + public ForceCloseSpannerFunction(long timeout, TimeUnit unit) { + this.timeout = timeout; + this.unit = unit; + } + + public Void apply(Spanner spanner) { + if (spanner instanceof SpannerImpl) { + ((SpannerImpl) spanner).close(timeout, unit); + } else { + spanner.close(); + } + return null; + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java index b2ebd826615..112621f8533 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java @@ -16,13 +16,16 @@ package com.google.cloud.spanner.connection; +import com.google.cloud.spanner.ForceCloseSpannerFunction; import com.google.cloud.spanner.MockSpannerServiceImpl; import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; +import com.google.cloud.spanner.RandomResultSetGenerator; import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.admin.database.v1.MockDatabaseAdminImpl; import com.google.cloud.spanner.admin.instance.v1.MockInstanceAdminImpl; import com.google.cloud.spanner.connection.ITAbstractSpannerTest.AbortInterceptor; import com.google.cloud.spanner.connection.ITAbstractSpannerTest.ITConnection; +import com.google.cloud.spanner.connection.SpannerPool.CheckAndCloseSpannersMode; import com.google.common.util.concurrent.AbstractFuture; import com.google.longrunning.GetOperationRequest; import com.google.longrunning.Operation; @@ -49,6 +52,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import org.junit.After; import org.junit.AfterClass; @@ -113,6 +117,7 @@ public abstract class AbstractMockServerTest { private boolean futureParentHandlers; private boolean exceptionRunnableParentHandlers; private boolean nettyServerParentHandlers; + private boolean clientStreamParentHandlers; @BeforeClass public static void startStaticServer() throws IOException { @@ -152,9 +157,7 @@ public void getOperation( @AfterClass public static void stopServer() throws Exception { - SpannerPool.closeSpannerPool(); server.shutdown(); - server.awaitTermination(); } @Before @@ -169,22 +172,30 @@ public void setupResults() { nettyServerParentHandlers = Logger.getLogger("io.grpc.netty.shaded.io.grpc.netty.NettyServerHandler") .getUseParentHandlers(); + clientStreamParentHandlers = + Logger.getLogger("io.grpc.netty.shaded.io.grpc.netty.NettyServerHandler") + .getUseParentHandlers(); Logger.getLogger(AbstractFuture.class.getName()).setUseParentHandlers(false); Logger.getLogger(LogExceptionRunnable.class.getName()).setUseParentHandlers(false); Logger.getLogger("io.grpc.netty.shaded.io.grpc.netty.NettyServerHandler") .setUseParentHandlers(false); + Logger.getLogger("io.grpc.internal.AbstractClientStream").setUseParentHandlers(false); } @After public void closeSpannerPool() { try { - SpannerPool.closeSpannerPool(); + SpannerPool.INSTANCE.checkAndCloseSpanners( + CheckAndCloseSpannersMode.ERROR, + new ForceCloseSpannerFunction(100L, TimeUnit.MILLISECONDS)); } finally { Logger.getLogger(AbstractFuture.class.getName()).setUseParentHandlers(futureParentHandlers); Logger.getLogger(LogExceptionRunnable.class.getName()) .setUseParentHandlers(exceptionRunnableParentHandlers); Logger.getLogger("io.grpc.netty.shaded.io.grpc.netty.NettyServerHandler") .setUseParentHandlers(nettyServerParentHandlers); + Logger.getLogger("io.grpc.internal.AbstractClientStream") + .setUseParentHandlers(clientStreamParentHandlers); } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java index 9174ef81b22..4e1238077e2 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java @@ -17,12 +17,15 @@ package com.google.cloud.spanner.connection; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.google.auth.Credentials; import com.google.cloud.NoCredentials; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.SessionPoolOptions; @@ -430,8 +433,9 @@ public void testSpannerPoolKeyEquality() { ConnectionOptions options1 = ConnectionOptions.newBuilder() .setUri( - "cloudspanner:/projects/p/instances/i/databases/d?minSessions=200;maxSessions=400") - .setCredentials(NoCredentials.getInstance()) + "cloudspanner://localhost:9010/projects/p1/instances/i/databases/d" + + "?minSessions=200;maxSessions=400;numChannels=8;usePlainText=true;userAgent=test-agent") + .setCredentials(mock(Credentials.class)) .build(); // options2 equals the default session pool options, and is therefore equal to ConnectionOptions // without any session pool configuration. @@ -451,8 +455,9 @@ public void testSpannerPoolKeyEquality() { SpannerPoolKey key2 = SpannerPoolKey.of(options2); SpannerPoolKey key3 = SpannerPoolKey.of(options3); - assertThat(key1).isNotEqualTo(key2); - assertThat(key2).isEqualTo(key3); - assertThat(key1).isNotEqualTo(key3); + assertFalse(key1.equals(key2)); + assertTrue(key2.equals(key3)); + assertFalse(key1.equals(key3)); + assertFalse(key1.equals(new Object())); } } From 7cdfb82b40487600547d0bad92119508161ca689 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Mon, 8 Feb 2021 17:22:10 -0800 Subject: [PATCH 30/42] feat: generate sample code in the Java microgenerator (#859) Committer: @miraleung PiperOrigin-RevId: 356341083 Source-Author: Google APIs Source-Date: Mon Feb 8 13:33:28 2021 -0800 Source-Repo: googleapis/googleapis Source-Sha: 8d8c008e56f1af31d57f75561e0f1848ffb29eeb Source-Link: https://github.com/googleapis/googleapis/commit/8d8c008e56f1af31d57f75561e0f1848ffb29eeb --- .../database/v1/DatabaseAdminClient.java | 970 ++++++++++++++++++ .../admin/database/v1/package-info.java | 7 + .../instance/v1/InstanceAdminClient.java | 541 ++++++++++ .../admin/instance/v1/package-info.java | 7 + .../cloud/spanner/v1/SpannerClient.java | 675 ++++++++++++ .../google/cloud/spanner/v1/package-info.java | 7 + synth.metadata | 6 +- 7 files changed, 2210 insertions(+), 3 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClient.java index cab26e07702..9e829e30d4e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClient.java @@ -85,6 +85,13 @@ *

    This class provides the ability to make remote calls to the backing service through method * calls that map to API methods. Sample code to get started: * + *

    {@code
    + * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    + *   DatabaseName name = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
    + *   Database response = databaseAdminClient.getDatabase(name);
    + * }
    + * }
    + * *

    Note: close() needs to be called on the DatabaseAdminClient object to clean up resources such * as threads. In the example above, try-with-resources is used, which automatically calls close(). * @@ -199,6 +206,17 @@ public final OperationsClient getOperationsClient() { /** * Lists Cloud Spanner databases. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   InstanceName parent = InstanceName.of("[PROJECT]", "[INSTANCE]");
    +   *   for (Database element : databaseAdminClient.listDatabases(parent).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param parent Required. The instance whose databases should be listed. Values are of the form * `projects/<project>/instances/<instance>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -215,6 +233,17 @@ public final ListDatabasesPagedResponse listDatabases(InstanceName parent) { /** * Lists Cloud Spanner databases. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String parent = InstanceName.of("[PROJECT]", "[INSTANCE]").toString();
    +   *   for (Database element : databaseAdminClient.listDatabases(parent).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param parent Required. The instance whose databases should be listed. Values are of the form * `projects/<project>/instances/<instance>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -228,6 +257,22 @@ public final ListDatabasesPagedResponse listDatabases(String parent) { /** * Lists Cloud Spanner databases. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   ListDatabasesRequest request =
    +   *       ListDatabasesRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setPageSize(883849137)
    +   *           .setPageToken("pageToken873572522")
    +   *           .build();
    +   *   for (Database element : databaseAdminClient.listDatabases(request).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -240,6 +285,23 @@ public final ListDatabasesPagedResponse listDatabases(ListDatabasesRequest reque * Lists Cloud Spanner databases. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   ListDatabasesRequest request =
    +   *       ListDatabasesRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setPageSize(883849137)
    +   *           .setPageToken("pageToken873572522")
    +   *           .build();
    +   *   ApiFuture future =
    +   *       databaseAdminClient.listDatabasesPagedCallable().futureCall(request);
    +   *   // Do something.
    +   *   for (Database element : future.get().iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    */ public final UnaryCallable listDatabasesPagedCallable() { @@ -251,6 +313,23 @@ public final ListDatabasesPagedResponse listDatabases(ListDatabasesRequest reque * Lists Cloud Spanner databases. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   while (true) {
    +   *     ListDatabasesResponse response = databaseAdminClient.listDatabasesCallable().call(request);
    +   *     for (Database element : response.getResponsesList()) {
    +   *       // doThingsWith(element);
    +   *     }
    +   *     String nextPageToken = response.getNextPageToken();
    +   *     if (!Strings.isNullOrEmpty(nextPageToken)) {
    +   *       request = request.toBuilder().setPageToken(nextPageToken).build();
    +   *     } else {
    +   *       break;
    +   *     }
    +   *   }
    +   * }
    +   * }
    */ public final UnaryCallable listDatabasesCallable() { return stub.listDatabasesCallable(); @@ -266,6 +345,16 @@ public final UnaryCallable listData * [response][google.longrunning.Operation.response] field type is * [Database][google.spanner.admin.database.v1.Database], if successful. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   InstanceName parent = InstanceName.of("[PROJECT]", "[INSTANCE]");
    +   *   String createStatement = "createStatement744686547";
    +   *   Database response = databaseAdminClient.createDatabaseAsync(parent, createStatement).get();
    +   * }
    +   * }
    + * * @param parent Required. The name of the instance that will serve the new database. Values are * of the form `projects/<project>/instances/<instance>`. * @param createStatement Required. A `CREATE DATABASE` statement, which specifies the ID of the @@ -295,6 +384,16 @@ public final OperationFuture createDatabaseAsy * [response][google.longrunning.Operation.response] field type is * [Database][google.spanner.admin.database.v1.Database], if successful. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String parent = InstanceName.of("[PROJECT]", "[INSTANCE]").toString();
    +   *   String createStatement = "createStatement744686547";
    +   *   Database response = databaseAdminClient.createDatabaseAsync(parent, createStatement).get();
    +   * }
    +   * }
    + * * @param parent Required. The name of the instance that will serve the new database. Values are * of the form `projects/<project>/instances/<instance>`. * @param createStatement Required. A `CREATE DATABASE` statement, which specifies the ID of the @@ -324,6 +423,20 @@ public final OperationFuture createDatabaseAsy * [response][google.longrunning.Operation.response] field type is * [Database][google.spanner.admin.database.v1.Database], if successful. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   CreateDatabaseRequest request =
    +   *       CreateDatabaseRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setCreateStatement("createStatement744686547")
    +   *           .addAllExtraStatements(new ArrayList())
    +   *           .build();
    +   *   Database response = databaseAdminClient.createDatabaseAsync(request).get();
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -343,6 +456,21 @@ public final OperationFuture createDatabaseAsy * [Database][google.spanner.admin.database.v1.Database], if successful. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   CreateDatabaseRequest request =
    +   *       CreateDatabaseRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setCreateStatement("createStatement744686547")
    +   *           .addAllExtraStatements(new ArrayList())
    +   *           .build();
    +   *   OperationFuture future =
    +   *       databaseAdminClient.createDatabaseOperationCallable().futureCall(request);
    +   *   // Do something.
    +   *   Database response = future.get();
    +   * }
    +   * }
    */ public final OperationCallable createDatabaseOperationCallable() { @@ -360,6 +488,21 @@ public final OperationFuture createDatabaseAsy * [Database][google.spanner.admin.database.v1.Database], if successful. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   CreateDatabaseRequest request =
    +   *       CreateDatabaseRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setCreateStatement("createStatement744686547")
    +   *           .addAllExtraStatements(new ArrayList())
    +   *           .build();
    +   *   ApiFuture future =
    +   *       databaseAdminClient.createDatabaseCallable().futureCall(request);
    +   *   // Do something.
    +   *   Operation response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable createDatabaseCallable() { return stub.createDatabaseCallable(); @@ -369,6 +512,15 @@ public final UnaryCallable createDatabaseCalla /** * Gets the state of a Cloud Spanner database. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   DatabaseName name = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
    +   *   Database response = databaseAdminClient.getDatabase(name);
    +   * }
    +   * }
    + * * @param name Required. The name of the requested database. Values are of the form * `projects/<project>/instances/<instance>/databases/<database>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -383,6 +535,15 @@ public final Database getDatabase(DatabaseName name) { /** * Gets the state of a Cloud Spanner database. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String name = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString();
    +   *   Database response = databaseAdminClient.getDatabase(name);
    +   * }
    +   * }
    + * * @param name Required. The name of the requested database. Values are of the form * `projects/<project>/instances/<instance>/databases/<database>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -396,6 +557,18 @@ public final Database getDatabase(String name) { /** * Gets the state of a Cloud Spanner database. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   GetDatabaseRequest request =
    +   *       GetDatabaseRequest.newBuilder()
    +   *           .setName(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .build();
    +   *   Database response = databaseAdminClient.getDatabase(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -408,6 +581,18 @@ public final Database getDatabase(GetDatabaseRequest request) { * Gets the state of a Cloud Spanner database. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   GetDatabaseRequest request =
    +   *       GetDatabaseRequest.newBuilder()
    +   *           .setName(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .build();
    +   *   ApiFuture future = databaseAdminClient.getDatabaseCallable().futureCall(request);
    +   *   // Do something.
    +   *   Database response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable getDatabaseCallable() { return stub.getDatabaseCallable(); @@ -423,6 +608,16 @@ public final UnaryCallable getDatabaseCallable() { * [UpdateDatabaseDdlMetadata][google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata]. The * operation has no response. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   DatabaseName database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
    +   *   List statements = new ArrayList<>();
    +   *   databaseAdminClient.updateDatabaseDdlAsync(database, statements).get();
    +   * }
    +   * }
    + * * @param database Required. The database to update. * @param statements Required. DDL statements to be applied to the database. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -447,6 +642,16 @@ public final OperationFuture updateDatabaseDdl * [UpdateDatabaseDdlMetadata][google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata]. The * operation has no response. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString();
    +   *   List statements = new ArrayList<>();
    +   *   databaseAdminClient.updateDatabaseDdlAsync(database, statements).get();
    +   * }
    +   * }
    + * * @param database Required. The database to update. * @param statements Required. DDL statements to be applied to the database. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -471,6 +676,20 @@ public final OperationFuture updateDatabaseDdl * [UpdateDatabaseDdlMetadata][google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata]. The * operation has no response. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   UpdateDatabaseDdlRequest request =
    +   *       UpdateDatabaseDdlRequest.newBuilder()
    +   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .addAllStatements(new ArrayList())
    +   *           .setOperationId("operationId129704162")
    +   *           .build();
    +   *   databaseAdminClient.updateDatabaseDdlAsync(request).get();
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -490,6 +709,21 @@ public final OperationFuture updateDatabaseDdl * operation has no response. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   UpdateDatabaseDdlRequest request =
    +   *       UpdateDatabaseDdlRequest.newBuilder()
    +   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .addAllStatements(new ArrayList())
    +   *           .setOperationId("operationId129704162")
    +   *           .build();
    +   *   OperationFuture future =
    +   *       databaseAdminClient.updateDatabaseDdlOperationCallable().futureCall(request);
    +   *   // Do something.
    +   *   future.get();
    +   * }
    +   * }
    */ public final OperationCallable updateDatabaseDdlOperationCallable() { @@ -507,6 +741,21 @@ public final OperationFuture updateDatabaseDdl * operation has no response. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   UpdateDatabaseDdlRequest request =
    +   *       UpdateDatabaseDdlRequest.newBuilder()
    +   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .addAllStatements(new ArrayList())
    +   *           .setOperationId("operationId129704162")
    +   *           .build();
    +   *   ApiFuture future =
    +   *       databaseAdminClient.updateDatabaseDdlCallable().futureCall(request);
    +   *   // Do something.
    +   *   future.get();
    +   * }
    +   * }
    */ public final UnaryCallable updateDatabaseDdlCallable() { return stub.updateDatabaseDdlCallable(); @@ -517,6 +766,15 @@ public final UnaryCallable updateDatabaseDd * Drops (aka deletes) a Cloud Spanner database. Completed backups for the database will be * retained according to their `expire_time`. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   DatabaseName database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
    +   *   databaseAdminClient.dropDatabase(database);
    +   * }
    +   * }
    + * * @param database Required. The database to be dropped. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -533,6 +791,15 @@ public final void dropDatabase(DatabaseName database) { * Drops (aka deletes) a Cloud Spanner database. Completed backups for the database will be * retained according to their `expire_time`. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString();
    +   *   databaseAdminClient.dropDatabase(database);
    +   * }
    +   * }
    + * * @param database Required. The database to be dropped. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -546,6 +813,18 @@ public final void dropDatabase(String database) { * Drops (aka deletes) a Cloud Spanner database. Completed backups for the database will be * retained according to their `expire_time`. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   DropDatabaseRequest request =
    +   *       DropDatabaseRequest.newBuilder()
    +   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .build();
    +   *   databaseAdminClient.dropDatabase(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -559,6 +838,18 @@ public final void dropDatabase(DropDatabaseRequest request) { * retained according to their `expire_time`. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   DropDatabaseRequest request =
    +   *       DropDatabaseRequest.newBuilder()
    +   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .build();
    +   *   ApiFuture future = databaseAdminClient.dropDatabaseCallable().futureCall(request);
    +   *   // Do something.
    +   *   future.get();
    +   * }
    +   * }
    */ public final UnaryCallable dropDatabaseCallable() { return stub.dropDatabaseCallable(); @@ -570,6 +861,15 @@ public final UnaryCallable dropDatabaseCallable() { * method does not show pending schema updates, those may be queried using the * [Operations][google.longrunning.Operations] API. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   DatabaseName database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
    +   *   GetDatabaseDdlResponse response = databaseAdminClient.getDatabaseDdl(database);
    +   * }
    +   * }
    + * * @param database Required. The database whose schema we wish to get. Values are of the form * `projects/<project>/instances/<instance>/databases/<database>` * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -588,6 +888,15 @@ public final GetDatabaseDdlResponse getDatabaseDdl(DatabaseName database) { * method does not show pending schema updates, those may be queried using the * [Operations][google.longrunning.Operations] API. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString();
    +   *   GetDatabaseDdlResponse response = databaseAdminClient.getDatabaseDdl(database);
    +   * }
    +   * }
    + * * @param database Required. The database whose schema we wish to get. Values are of the form * `projects/<project>/instances/<instance>/databases/<database>` * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -604,6 +913,18 @@ public final GetDatabaseDdlResponse getDatabaseDdl(String database) { * method does not show pending schema updates, those may be queried using the * [Operations][google.longrunning.Operations] API. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   GetDatabaseDdlRequest request =
    +   *       GetDatabaseDdlRequest.newBuilder()
    +   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .build();
    +   *   GetDatabaseDdlResponse response = databaseAdminClient.getDatabaseDdl(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -618,6 +939,19 @@ public final GetDatabaseDdlResponse getDatabaseDdl(GetDatabaseDdlRequest request * [Operations][google.longrunning.Operations] API. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   GetDatabaseDdlRequest request =
    +   *       GetDatabaseDdlRequest.newBuilder()
    +   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .build();
    +   *   ApiFuture future =
    +   *       databaseAdminClient.getDatabaseDdlCallable().futureCall(request);
    +   *   // Do something.
    +   *   GetDatabaseDdlResponse response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable getDatabaseDdlCallable() { @@ -633,6 +967,16 @@ public final GetDatabaseDdlResponse getDatabaseDdl(GetDatabaseDdlRequest request * `spanner.backups.setIamPolicy` permission on * [resource][google.iam.v1.SetIamPolicyRequest.resource]. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   ResourceName resource = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
    +   *   Policy policy = Policy.newBuilder().build();
    +   *   Policy response = databaseAdminClient.setIamPolicy(resource, policy);
    +   * }
    +   * }
    + * * @param resource REQUIRED: The resource for which the policy is being specified. See the * operation documentation for the appropriate value for this field. * @param policy REQUIRED: The complete policy to be applied to the `resource`. The size of the @@ -658,6 +1002,16 @@ public final Policy setIamPolicy(ResourceName resource, Policy policy) { * `spanner.backups.setIamPolicy` permission on * [resource][google.iam.v1.SetIamPolicyRequest.resource]. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String resource = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString();
    +   *   Policy policy = Policy.newBuilder().build();
    +   *   Policy response = databaseAdminClient.setIamPolicy(resource, policy);
    +   * }
    +   * }
    + * * @param resource REQUIRED: The resource for which the policy is being specified. See the * operation documentation for the appropriate value for this field. * @param policy REQUIRED: The complete policy to be applied to the `resource`. The size of the @@ -680,6 +1034,19 @@ public final Policy setIamPolicy(String resource, Policy policy) { * `spanner.backups.setIamPolicy` permission on * [resource][google.iam.v1.SetIamPolicyRequest.resource]. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   SetIamPolicyRequest request =
    +   *       SetIamPolicyRequest.newBuilder()
    +   *           .setResource(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .setPolicy(Policy.newBuilder().build())
    +   *           .build();
    +   *   Policy response = databaseAdminClient.setIamPolicy(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -697,6 +1064,19 @@ public final Policy setIamPolicy(SetIamPolicyRequest request) { * [resource][google.iam.v1.SetIamPolicyRequest.resource]. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   SetIamPolicyRequest request =
    +   *       SetIamPolicyRequest.newBuilder()
    +   *           .setResource(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .setPolicy(Policy.newBuilder().build())
    +   *           .build();
    +   *   ApiFuture future = databaseAdminClient.setIamPolicyCallable().futureCall(request);
    +   *   // Do something.
    +   *   Policy response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable setIamPolicyCallable() { return stub.setIamPolicyCallable(); @@ -712,6 +1092,15 @@ public final UnaryCallable setIamPolicyCallable() { * `spanner.backups.getIamPolicy` permission on * [resource][google.iam.v1.GetIamPolicyRequest.resource]. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   ResourceName resource = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
    +   *   Policy response = databaseAdminClient.getIamPolicy(resource);
    +   * }
    +   * }
    + * * @param resource REQUIRED: The resource for which the policy is being requested. See the * operation documentation for the appropriate value for this field. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -734,6 +1123,15 @@ public final Policy getIamPolicy(ResourceName resource) { * `spanner.backups.getIamPolicy` permission on * [resource][google.iam.v1.GetIamPolicyRequest.resource]. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String resource = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString();
    +   *   Policy response = databaseAdminClient.getIamPolicy(resource);
    +   * }
    +   * }
    + * * @param resource REQUIRED: The resource for which the policy is being requested. See the * operation documentation for the appropriate value for this field. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -753,6 +1151,19 @@ public final Policy getIamPolicy(String resource) { * `spanner.backups.getIamPolicy` permission on * [resource][google.iam.v1.GetIamPolicyRequest.resource]. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   GetIamPolicyRequest request =
    +   *       GetIamPolicyRequest.newBuilder()
    +   *           .setResource(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .setOptions(GetPolicyOptions.newBuilder().build())
    +   *           .build();
    +   *   Policy response = databaseAdminClient.getIamPolicy(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -771,6 +1182,19 @@ public final Policy getIamPolicy(GetIamPolicyRequest request) { * [resource][google.iam.v1.GetIamPolicyRequest.resource]. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   GetIamPolicyRequest request =
    +   *       GetIamPolicyRequest.newBuilder()
    +   *           .setResource(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .setOptions(GetPolicyOptions.newBuilder().build())
    +   *           .build();
    +   *   ApiFuture future = databaseAdminClient.getIamPolicyCallable().futureCall(request);
    +   *   // Do something.
    +   *   Policy response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable getIamPolicyCallable() { return stub.getIamPolicyCallable(); @@ -786,6 +1210,17 @@ public final UnaryCallable getIamPolicyCallable() { * does not exist will result in a NOT_FOUND error if the user has `spanner.backups.list` * permission on the containing instance. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   ResourceName resource = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
    +   *   List permissions = new ArrayList<>();
    +   *   TestIamPermissionsResponse response =
    +   *       databaseAdminClient.testIamPermissions(resource, permissions);
    +   * }
    +   * }
    + * * @param resource REQUIRED: The resource for which the policy detail is being requested. See the * operation documentation for the appropriate value for this field. * @param permissions The set of permissions to check for the `resource`. Permissions with @@ -813,6 +1248,17 @@ public final TestIamPermissionsResponse testIamPermissions( * does not exist will result in a NOT_FOUND error if the user has `spanner.backups.list` * permission on the containing instance. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String resource = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString();
    +   *   List permissions = new ArrayList<>();
    +   *   TestIamPermissionsResponse response =
    +   *       databaseAdminClient.testIamPermissions(resource, permissions);
    +   * }
    +   * }
    + * * @param resource REQUIRED: The resource for which the policy detail is being requested. See the * operation documentation for the appropriate value for this field. * @param permissions The set of permissions to check for the `resource`. Permissions with @@ -840,6 +1286,19 @@ public final TestIamPermissionsResponse testIamPermissions( * does not exist will result in a NOT_FOUND error if the user has `spanner.backups.list` * permission on the containing instance. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   TestIamPermissionsRequest request =
    +   *       TestIamPermissionsRequest.newBuilder()
    +   *           .setResource(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .addAllPermissions(new ArrayList())
    +   *           .build();
    +   *   TestIamPermissionsResponse response = databaseAdminClient.testIamPermissions(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -858,6 +1317,20 @@ public final TestIamPermissionsResponse testIamPermissions(TestIamPermissionsReq * permission on the containing instance. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   TestIamPermissionsRequest request =
    +   *       TestIamPermissionsRequest.newBuilder()
    +   *           .setResource(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .addAllPermissions(new ArrayList())
    +   *           .build();
    +   *   ApiFuture future =
    +   *       databaseAdminClient.testIamPermissionsCallable().futureCall(request);
    +   *   // Do something.
    +   *   TestIamPermissionsResponse response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable testIamPermissionsCallable() { @@ -877,6 +1350,17 @@ public final TestIamPermissionsResponse testIamPermissions(TestIamPermissionsReq * operation will stop the creation and delete the backup. There can be only one pending backup * creation per database. Backup creation of different databases can run concurrently. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   InstanceName parent = InstanceName.of("[PROJECT]", "[INSTANCE]");
    +   *   Backup backup = Backup.newBuilder().build();
    +   *   String backupId = "backupId2121930365";
    +   *   Backup response = databaseAdminClient.createBackupAsync(parent, backup, backupId).get();
    +   * }
    +   * }
    + * * @param parent Required. The name of the instance in which the backup will be created. This must * be the same instance that contains the database the backup will be created from. The backup * will be stored in the location(s) specified in the instance configuration of this instance. @@ -911,6 +1395,17 @@ public final OperationFuture createBackupAsync( * operation will stop the creation and delete the backup. There can be only one pending backup * creation per database. Backup creation of different databases can run concurrently. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String parent = InstanceName.of("[PROJECT]", "[INSTANCE]").toString();
    +   *   Backup backup = Backup.newBuilder().build();
    +   *   String backupId = "backupId2121930365";
    +   *   Backup response = databaseAdminClient.createBackupAsync(parent, backup, backupId).get();
    +   * }
    +   * }
    + * * @param parent Required. The name of the instance in which the backup will be created. This must * be the same instance that contains the database the backup will be created from. The backup * will be stored in the location(s) specified in the instance configuration of this instance. @@ -945,6 +1440,20 @@ public final OperationFuture createBackupAsync( * operation will stop the creation and delete the backup. There can be only one pending backup * creation per database. Backup creation of different databases can run concurrently. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   CreateBackupRequest request =
    +   *       CreateBackupRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setBackupId("backupId2121930365")
    +   *           .setBackup(Backup.newBuilder().build())
    +   *           .build();
    +   *   Backup response = databaseAdminClient.createBackupAsync(request).get();
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -967,6 +1476,21 @@ public final OperationFuture createBackupAsync( * creation per database. Backup creation of different databases can run concurrently. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   CreateBackupRequest request =
    +   *       CreateBackupRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setBackupId("backupId2121930365")
    +   *           .setBackup(Backup.newBuilder().build())
    +   *           .build();
    +   *   OperationFuture future =
    +   *       databaseAdminClient.createBackupOperationCallable().futureCall(request);
    +   *   // Do something.
    +   *   Backup response = future.get();
    +   * }
    +   * }
    */ public final OperationCallable createBackupOperationCallable() { @@ -987,6 +1511,20 @@ public final OperationFuture createBackupAsync( * creation per database. Backup creation of different databases can run concurrently. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   CreateBackupRequest request =
    +   *       CreateBackupRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setBackupId("backupId2121930365")
    +   *           .setBackup(Backup.newBuilder().build())
    +   *           .build();
    +   *   ApiFuture future = databaseAdminClient.createBackupCallable().futureCall(request);
    +   *   // Do something.
    +   *   Operation response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable createBackupCallable() { return stub.createBackupCallable(); @@ -996,6 +1534,15 @@ public final UnaryCallable createBackupCallable( /** * Gets metadata on a pending or completed [Backup][google.spanner.admin.database.v1.Backup]. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   BackupName name = BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]");
    +   *   Backup response = databaseAdminClient.getBackup(name);
    +   * }
    +   * }
    + * * @param name Required. Name of the backup. Values are of the form * `projects/<project>/instances/<instance>/backups/<backup>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -1010,6 +1557,15 @@ public final Backup getBackup(BackupName name) { /** * Gets metadata on a pending or completed [Backup][google.spanner.admin.database.v1.Backup]. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String name = BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]").toString();
    +   *   Backup response = databaseAdminClient.getBackup(name);
    +   * }
    +   * }
    + * * @param name Required. Name of the backup. Values are of the form * `projects/<project>/instances/<instance>/backups/<backup>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -1023,6 +1579,18 @@ public final Backup getBackup(String name) { /** * Gets metadata on a pending or completed [Backup][google.spanner.admin.database.v1.Backup]. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   GetBackupRequest request =
    +   *       GetBackupRequest.newBuilder()
    +   *           .setName(BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]").toString())
    +   *           .build();
    +   *   Backup response = databaseAdminClient.getBackup(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -1035,6 +1603,18 @@ public final Backup getBackup(GetBackupRequest request) { * Gets metadata on a pending or completed [Backup][google.spanner.admin.database.v1.Backup]. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   GetBackupRequest request =
    +   *       GetBackupRequest.newBuilder()
    +   *           .setName(BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]").toString())
    +   *           .build();
    +   *   ApiFuture future = databaseAdminClient.getBackupCallable().futureCall(request);
    +   *   // Do something.
    +   *   Backup response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable getBackupCallable() { return stub.getBackupCallable(); @@ -1044,6 +1624,16 @@ public final UnaryCallable getBackupCallable() { /** * Updates a pending or completed [Backup][google.spanner.admin.database.v1.Backup]. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   Backup backup = Backup.newBuilder().build();
    +   *   FieldMask updateMask = FieldMask.newBuilder().build();
    +   *   Backup response = databaseAdminClient.updateBackup(backup, updateMask);
    +   * }
    +   * }
    + * * @param backup Required. The backup to update. `backup.name`, and the fields to be updated as * specified by `update_mask` are required. Other fields are ignored. Update is only supported * for the following fields: * `backup.expire_time`. @@ -1063,6 +1653,19 @@ public final Backup updateBackup(Backup backup, FieldMask updateMask) { /** * Updates a pending or completed [Backup][google.spanner.admin.database.v1.Backup]. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   UpdateBackupRequest request =
    +   *       UpdateBackupRequest.newBuilder()
    +   *           .setBackup(Backup.newBuilder().build())
    +   *           .setUpdateMask(FieldMask.newBuilder().build())
    +   *           .build();
    +   *   Backup response = databaseAdminClient.updateBackup(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -1075,6 +1678,19 @@ public final Backup updateBackup(UpdateBackupRequest request) { * Updates a pending or completed [Backup][google.spanner.admin.database.v1.Backup]. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   UpdateBackupRequest request =
    +   *       UpdateBackupRequest.newBuilder()
    +   *           .setBackup(Backup.newBuilder().build())
    +   *           .setUpdateMask(FieldMask.newBuilder().build())
    +   *           .build();
    +   *   ApiFuture future = databaseAdminClient.updateBackupCallable().futureCall(request);
    +   *   // Do something.
    +   *   Backup response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable updateBackupCallable() { return stub.updateBackupCallable(); @@ -1084,6 +1700,15 @@ public final UnaryCallable updateBackupCallable() { /** * Deletes a pending or completed [Backup][google.spanner.admin.database.v1.Backup]. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   BackupName name = BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]");
    +   *   databaseAdminClient.deleteBackup(name);
    +   * }
    +   * }
    + * * @param name Required. Name of the backup to delete. Values are of the form * `projects/<project>/instances/<instance>/backups/<backup>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -1098,6 +1723,15 @@ public final void deleteBackup(BackupName name) { /** * Deletes a pending or completed [Backup][google.spanner.admin.database.v1.Backup]. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String name = BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]").toString();
    +   *   databaseAdminClient.deleteBackup(name);
    +   * }
    +   * }
    + * * @param name Required. Name of the backup to delete. Values are of the form * `projects/<project>/instances/<instance>/backups/<backup>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -1111,6 +1745,18 @@ public final void deleteBackup(String name) { /** * Deletes a pending or completed [Backup][google.spanner.admin.database.v1.Backup]. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   DeleteBackupRequest request =
    +   *       DeleteBackupRequest.newBuilder()
    +   *           .setName(BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]").toString())
    +   *           .build();
    +   *   databaseAdminClient.deleteBackup(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -1123,6 +1769,18 @@ public final void deleteBackup(DeleteBackupRequest request) { * Deletes a pending or completed [Backup][google.spanner.admin.database.v1.Backup]. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   DeleteBackupRequest request =
    +   *       DeleteBackupRequest.newBuilder()
    +   *           .setName(BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]").toString())
    +   *           .build();
    +   *   ApiFuture future = databaseAdminClient.deleteBackupCallable().futureCall(request);
    +   *   // Do something.
    +   *   future.get();
    +   * }
    +   * }
    */ public final UnaryCallable deleteBackupCallable() { return stub.deleteBackupCallable(); @@ -1133,6 +1791,17 @@ public final UnaryCallable deleteBackupCallable() { * Lists completed and pending backups. Backups returned are ordered by `create_time` in * descending order, starting from the most recent `create_time`. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   InstanceName parent = InstanceName.of("[PROJECT]", "[INSTANCE]");
    +   *   for (Backup element : databaseAdminClient.listBackups(parent).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param parent Required. The instance to list backups from. Values are of the form * `projects/<project>/instances/<instance>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -1150,6 +1819,17 @@ public final ListBackupsPagedResponse listBackups(InstanceName parent) { * Lists completed and pending backups. Backups returned are ordered by `create_time` in * descending order, starting from the most recent `create_time`. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String parent = InstanceName.of("[PROJECT]", "[INSTANCE]").toString();
    +   *   for (Backup element : databaseAdminClient.listBackups(parent).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param parent Required. The instance to list backups from. Values are of the form * `projects/<project>/instances/<instance>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -1164,6 +1844,23 @@ public final ListBackupsPagedResponse listBackups(String parent) { * Lists completed and pending backups. Backups returned are ordered by `create_time` in * descending order, starting from the most recent `create_time`. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   ListBackupsRequest request =
    +   *       ListBackupsRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setFilter("filter-1274492040")
    +   *           .setPageSize(883849137)
    +   *           .setPageToken("pageToken873572522")
    +   *           .build();
    +   *   for (Backup element : databaseAdminClient.listBackups(request).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -1177,6 +1874,23 @@ public final ListBackupsPagedResponse listBackups(ListBackupsRequest request) { * descending order, starting from the most recent `create_time`. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   ListBackupsRequest request =
    +   *       ListBackupsRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setFilter("filter-1274492040")
    +   *           .setPageSize(883849137)
    +   *           .setPageToken("pageToken873572522")
    +   *           .build();
    +   *   ApiFuture future = databaseAdminClient.listBackupsPagedCallable().futureCall(request);
    +   *   // Do something.
    +   *   for (Backup element : future.get().iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    */ public final UnaryCallable listBackupsPagedCallable() { @@ -1189,6 +1903,23 @@ public final ListBackupsPagedResponse listBackups(ListBackupsRequest request) { * descending order, starting from the most recent `create_time`. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   while (true) {
    +   *     ListBackupsResponse response = databaseAdminClient.listBackupsCallable().call(request);
    +   *     for (Backup element : response.getResponsesList()) {
    +   *       // doThingsWith(element);
    +   *     }
    +   *     String nextPageToken = response.getNextPageToken();
    +   *     if (!Strings.isNullOrEmpty(nextPageToken)) {
    +   *       request = request.toBuilder().setPageToken(nextPageToken).build();
    +   *     } else {
    +   *       break;
    +   *     }
    +   *   }
    +   * }
    +   * }
    */ public final UnaryCallable listBackupsCallable() { return stub.listBackupsCallable(); @@ -1211,6 +1942,18 @@ public final UnaryCallable listBackupsC * operation can be initiated, without waiting for the optimize operation associated with the * first restore to complete. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   InstanceName parent = InstanceName.of("[PROJECT]", "[INSTANCE]");
    +   *   String databaseId = "databaseId1688905718";
    +   *   BackupName backup = BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]");
    +   *   Database response =
    +   *       databaseAdminClient.restoreDatabaseAsync(parent, databaseId, backup).get();
    +   * }
    +   * }
    + * * @param parent Required. The name of the instance in which to create the restored database. This * instance must be in the same project and have the same instance configuration as the * instance containing the source backup. Values are of the form @@ -1251,6 +1994,18 @@ public final OperationFuture restoreDatabaseA * operation can be initiated, without waiting for the optimize operation associated with the * first restore to complete. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   InstanceName parent = InstanceName.of("[PROJECT]", "[INSTANCE]");
    +   *   String databaseId = "databaseId1688905718";
    +   *   String backup = BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]").toString();
    +   *   Database response =
    +   *       databaseAdminClient.restoreDatabaseAsync(parent, databaseId, backup).get();
    +   * }
    +   * }
    + * * @param parent Required. The name of the instance in which to create the restored database. This * instance must be in the same project and have the same instance configuration as the * instance containing the source backup. Values are of the form @@ -1291,6 +2046,18 @@ public final OperationFuture restoreDatabaseA * operation can be initiated, without waiting for the optimize operation associated with the * first restore to complete. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String parent = InstanceName.of("[PROJECT]", "[INSTANCE]").toString();
    +   *   String databaseId = "databaseId1688905718";
    +   *   BackupName backup = BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]");
    +   *   Database response =
    +   *       databaseAdminClient.restoreDatabaseAsync(parent, databaseId, backup).get();
    +   * }
    +   * }
    + * * @param parent Required. The name of the instance in which to create the restored database. This * instance must be in the same project and have the same instance configuration as the * instance containing the source backup. Values are of the form @@ -1331,6 +2098,18 @@ public final OperationFuture restoreDatabaseA * operation can be initiated, without waiting for the optimize operation associated with the * first restore to complete. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String parent = InstanceName.of("[PROJECT]", "[INSTANCE]").toString();
    +   *   String databaseId = "databaseId1688905718";
    +   *   String backup = BackupName.of("[PROJECT]", "[INSTANCE]", "[BACKUP]").toString();
    +   *   Database response =
    +   *       databaseAdminClient.restoreDatabaseAsync(parent, databaseId, backup).get();
    +   * }
    +   * }
    + * * @param parent Required. The name of the instance in which to create the restored database. This * instance must be in the same project and have the same instance configuration as the * instance containing the source backup. Values are of the form @@ -1371,6 +2150,19 @@ public final OperationFuture restoreDatabaseA * operation can be initiated, without waiting for the optimize operation associated with the * first restore to complete. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   RestoreDatabaseRequest request =
    +   *       RestoreDatabaseRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setDatabaseId("databaseId1688905718")
    +   *           .build();
    +   *   Database response = databaseAdminClient.restoreDatabaseAsync(request).get();
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -1397,6 +2189,20 @@ public final OperationFuture restoreDatabaseA * first restore to complete. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   RestoreDatabaseRequest request =
    +   *       RestoreDatabaseRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setDatabaseId("databaseId1688905718")
    +   *           .build();
    +   *   OperationFuture future =
    +   *       databaseAdminClient.restoreDatabaseOperationCallable().futureCall(request);
    +   *   // Do something.
    +   *   Database response = future.get();
    +   * }
    +   * }
    */ public final OperationCallable restoreDatabaseOperationCallable() { @@ -1421,6 +2227,20 @@ public final OperationFuture restoreDatabaseA * first restore to complete. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   RestoreDatabaseRequest request =
    +   *       RestoreDatabaseRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setDatabaseId("databaseId1688905718")
    +   *           .build();
    +   *   ApiFuture future =
    +   *       databaseAdminClient.restoreDatabaseCallable().futureCall(request);
    +   *   // Do something.
    +   *   Operation response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable restoreDatabaseCallable() { return stub.restoreDatabaseCallable(); @@ -1435,6 +2255,17 @@ public final UnaryCallable restoreDatabaseCal * `metadata.type_url` describes the type of the metadata. Operations returned include those that * have completed/failed/canceled within the last 7 days, and pending operations. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   InstanceName parent = InstanceName.of("[PROJECT]", "[INSTANCE]");
    +   *   for (Operation element : databaseAdminClient.listDatabaseOperations(parent).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param parent Required. The instance of the database operations. Values are of the form * `projects/<project>/instances/<instance>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -1456,6 +2287,17 @@ public final ListDatabaseOperationsPagedResponse listDatabaseOperations(Instance * `metadata.type_url` describes the type of the metadata. Operations returned include those that * have completed/failed/canceled within the last 7 days, and pending operations. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String parent = InstanceName.of("[PROJECT]", "[INSTANCE]").toString();
    +   *   for (Operation element : databaseAdminClient.listDatabaseOperations(parent).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param parent Required. The instance of the database operations. Values are of the form * `projects/<project>/instances/<instance>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -1475,6 +2317,23 @@ public final ListDatabaseOperationsPagedResponse listDatabaseOperations(String p * `metadata.type_url` describes the type of the metadata. Operations returned include those that * have completed/failed/canceled within the last 7 days, and pending operations. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   ListDatabaseOperationsRequest request =
    +   *       ListDatabaseOperationsRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setFilter("filter-1274492040")
    +   *           .setPageSize(883849137)
    +   *           .setPageToken("pageToken873572522")
    +   *           .build();
    +   *   for (Operation element : databaseAdminClient.listDatabaseOperations(request).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -1493,6 +2352,24 @@ public final ListDatabaseOperationsPagedResponse listDatabaseOperations( * have completed/failed/canceled within the last 7 days, and pending operations. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   ListDatabaseOperationsRequest request =
    +   *       ListDatabaseOperationsRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setFilter("filter-1274492040")
    +   *           .setPageSize(883849137)
    +   *           .setPageToken("pageToken873572522")
    +   *           .build();
    +   *   ApiFuture future =
    +   *       databaseAdminClient.listDatabaseOperationsPagedCallable().futureCall(request);
    +   *   // Do something.
    +   *   for (Operation element : future.get().iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    */ public final UnaryCallable listDatabaseOperationsPagedCallable() { @@ -1509,6 +2386,24 @@ public final ListDatabaseOperationsPagedResponse listDatabaseOperations( * have completed/failed/canceled within the last 7 days, and pending operations. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   while (true) {
    +   *     ListDatabaseOperationsResponse response =
    +   *         databaseAdminClient.listDatabaseOperationsCallable().call(request);
    +   *     for (Operation element : response.getResponsesList()) {
    +   *       // doThingsWith(element);
    +   *     }
    +   *     String nextPageToken = response.getNextPageToken();
    +   *     if (!Strings.isNullOrEmpty(nextPageToken)) {
    +   *       request = request.toBuilder().setPageToken(nextPageToken).build();
    +   *     } else {
    +   *       break;
    +   *     }
    +   *   }
    +   * }
    +   * }
    */ public final UnaryCallable listDatabaseOperationsCallable() { @@ -1526,6 +2421,17 @@ public final ListDatabaseOperationsPagedResponse listDatabaseOperations( * returned are ordered by `operation.metadata.value.progress.start_time` in descending order * starting from the most recently started operation. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   InstanceName parent = InstanceName.of("[PROJECT]", "[INSTANCE]");
    +   *   for (Operation element : databaseAdminClient.listBackupOperations(parent).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param parent Required. The instance of the backup operations. Values are of the form * `projects/<project>/instances/<instance>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -1549,6 +2455,17 @@ public final ListBackupOperationsPagedResponse listBackupOperations(InstanceName * returned are ordered by `operation.metadata.value.progress.start_time` in descending order * starting from the most recently started operation. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   String parent = InstanceName.of("[PROJECT]", "[INSTANCE]").toString();
    +   *   for (Operation element : databaseAdminClient.listBackupOperations(parent).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param parent Required. The instance of the backup operations. Values are of the form * `projects/<project>/instances/<instance>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -1570,6 +2487,23 @@ public final ListBackupOperationsPagedResponse listBackupOperations(String paren * returned are ordered by `operation.metadata.value.progress.start_time` in descending order * starting from the most recently started operation. * + *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   ListBackupOperationsRequest request =
    +   *       ListBackupOperationsRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setFilter("filter-1274492040")
    +   *           .setPageSize(883849137)
    +   *           .setPageToken("pageToken873572522")
    +   *           .build();
    +   *   for (Operation element : databaseAdminClient.listBackupOperations(request).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -1590,6 +2524,24 @@ public final ListBackupOperationsPagedResponse listBackupOperations( * starting from the most recently started operation. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   ListBackupOperationsRequest request =
    +   *       ListBackupOperationsRequest.newBuilder()
    +   *           .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setFilter("filter-1274492040")
    +   *           .setPageSize(883849137)
    +   *           .setPageToken("pageToken873572522")
    +   *           .build();
    +   *   ApiFuture future =
    +   *       databaseAdminClient.listBackupOperationsPagedCallable().futureCall(request);
    +   *   // Do something.
    +   *   for (Operation element : future.get().iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    */ public final UnaryCallable listBackupOperationsPagedCallable() { @@ -1608,6 +2560,24 @@ public final ListBackupOperationsPagedResponse listBackupOperations( * starting from the most recently started operation. * *

    Sample code: + * + *

    {@code
    +   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    +   *   while (true) {
    +   *     ListBackupOperationsResponse response =
    +   *         databaseAdminClient.listBackupOperationsCallable().call(request);
    +   *     for (Operation element : response.getResponsesList()) {
    +   *       // doThingsWith(element);
    +   *     }
    +   *     String nextPageToken = response.getNextPageToken();
    +   *     if (!Strings.isNullOrEmpty(nextPageToken)) {
    +   *       request = request.toBuilder().setPageToken(nextPageToken).build();
    +   *     } else {
    +   *       break;
    +   *     }
    +   *   }
    +   * }
    +   * }
    */ public final UnaryCallable listBackupOperationsCallable() { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/package-info.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/package-info.java index a7a4a8722e0..6723f47e828 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/package-info.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/package-info.java @@ -26,6 +26,13 @@ * list backups for a database and to restore from an existing backup. * *

    Sample for DatabaseAdminClient: + * + *

    {@code
    + * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
    + *   DatabaseName name = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
    + *   Database response = databaseAdminClient.getDatabase(name);
    + * }
    + * }
    */ @Generated("by gapic-generator-java") package com.google.cloud.spanner.admin.database.v1; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/InstanceAdminClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/InstanceAdminClient.java index 6494e0eac90..68c0be81db8 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/InstanceAdminClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/InstanceAdminClient.java @@ -85,6 +85,13 @@ *

    This class provides the ability to make remote calls to the backing service through method * calls that map to API methods. Sample code to get started: * + *

    {@code
    + * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    + *   InstanceConfigName name = InstanceConfigName.of("[PROJECT]", "[INSTANCE_CONFIG]");
    + *   InstanceConfig response = instanceAdminClient.getInstanceConfig(name);
    + * }
    + * }
    + * *

    Note: close() needs to be called on the InstanceAdminClient object to clean up resources such * as threads. In the example above, try-with-resources is used, which automatically calls close(). * @@ -199,6 +206,17 @@ public final OperationsClient getOperationsClient() { /** * Lists the supported instance configurations for a given project. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   ProjectName parent = ProjectName.of("[PROJECT]");
    +   *   for (InstanceConfig element : instanceAdminClient.listInstanceConfigs(parent).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param parent Required. The name of the project for which a list of supported instance * configurations is requested. Values are of the form `projects/<project>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -215,6 +233,17 @@ public final ListInstanceConfigsPagedResponse listInstanceConfigs(ProjectName pa /** * Lists the supported instance configurations for a given project. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   String parent = ProjectName.of("[PROJECT]").toString();
    +   *   for (InstanceConfig element : instanceAdminClient.listInstanceConfigs(parent).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param parent Required. The name of the project for which a list of supported instance * configurations is requested. Values are of the form `projects/<project>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -229,6 +258,22 @@ public final ListInstanceConfigsPagedResponse listInstanceConfigs(String parent) /** * Lists the supported instance configurations for a given project. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   ListInstanceConfigsRequest request =
    +   *       ListInstanceConfigsRequest.newBuilder()
    +   *           .setParent(ProjectName.of("[PROJECT]").toString())
    +   *           .setPageSize(883849137)
    +   *           .setPageToken("pageToken873572522")
    +   *           .build();
    +   *   for (InstanceConfig element : instanceAdminClient.listInstanceConfigs(request).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -242,6 +287,23 @@ public final ListInstanceConfigsPagedResponse listInstanceConfigs( * Lists the supported instance configurations for a given project. * *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   ListInstanceConfigsRequest request =
    +   *       ListInstanceConfigsRequest.newBuilder()
    +   *           .setParent(ProjectName.of("[PROJECT]").toString())
    +   *           .setPageSize(883849137)
    +   *           .setPageToken("pageToken873572522")
    +   *           .build();
    +   *   ApiFuture future =
    +   *       instanceAdminClient.listInstanceConfigsPagedCallable().futureCall(request);
    +   *   // Do something.
    +   *   for (InstanceConfig element : future.get().iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    */ public final UnaryCallable listInstanceConfigsPagedCallable() { @@ -253,6 +315,24 @@ public final ListInstanceConfigsPagedResponse listInstanceConfigs( * Lists the supported instance configurations for a given project. * *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   while (true) {
    +   *     ListInstanceConfigsResponse response =
    +   *         instanceAdminClient.listInstanceConfigsCallable().call(request);
    +   *     for (InstanceConfig element : response.getResponsesList()) {
    +   *       // doThingsWith(element);
    +   *     }
    +   *     String nextPageToken = response.getNextPageToken();
    +   *     if (!Strings.isNullOrEmpty(nextPageToken)) {
    +   *       request = request.toBuilder().setPageToken(nextPageToken).build();
    +   *     } else {
    +   *       break;
    +   *     }
    +   *   }
    +   * }
    +   * }
    */ public final UnaryCallable listInstanceConfigsCallable() { @@ -263,6 +343,15 @@ public final ListInstanceConfigsPagedResponse listInstanceConfigs( /** * Gets information about a particular instance configuration. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   InstanceConfigName name = InstanceConfigName.of("[PROJECT]", "[INSTANCE_CONFIG]");
    +   *   InstanceConfig response = instanceAdminClient.getInstanceConfig(name);
    +   * }
    +   * }
    + * * @param name Required. The name of the requested instance configuration. Values are of the form * `projects/<project>/instanceConfigs/<config>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -279,6 +368,15 @@ public final InstanceConfig getInstanceConfig(InstanceConfigName name) { /** * Gets information about a particular instance configuration. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   String name = InstanceConfigName.of("[PROJECT]", "[INSTANCE_CONFIG]").toString();
    +   *   InstanceConfig response = instanceAdminClient.getInstanceConfig(name);
    +   * }
    +   * }
    + * * @param name Required. The name of the requested instance configuration. Values are of the form * `projects/<project>/instanceConfigs/<config>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -292,6 +390,18 @@ public final InstanceConfig getInstanceConfig(String name) { /** * Gets information about a particular instance configuration. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   GetInstanceConfigRequest request =
    +   *       GetInstanceConfigRequest.newBuilder()
    +   *           .setName(InstanceConfigName.of("[PROJECT]", "[INSTANCE_CONFIG]").toString())
    +   *           .build();
    +   *   InstanceConfig response = instanceAdminClient.getInstanceConfig(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -304,6 +414,19 @@ public final InstanceConfig getInstanceConfig(GetInstanceConfigRequest request) * Gets information about a particular instance configuration. * *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   GetInstanceConfigRequest request =
    +   *       GetInstanceConfigRequest.newBuilder()
    +   *           .setName(InstanceConfigName.of("[PROJECT]", "[INSTANCE_CONFIG]").toString())
    +   *           .build();
    +   *   ApiFuture future =
    +   *       instanceAdminClient.getInstanceConfigCallable().futureCall(request);
    +   *   // Do something.
    +   *   InstanceConfig response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable getInstanceConfigCallable() { return stub.getInstanceConfigCallable(); @@ -313,6 +436,17 @@ public final UnaryCallable getInstance /** * Lists all instances in the given project. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   ProjectName parent = ProjectName.of("[PROJECT]");
    +   *   for (Instance element : instanceAdminClient.listInstances(parent).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param parent Required. The name of the project for which a list of instances is requested. * Values are of the form `projects/<project>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -329,6 +463,17 @@ public final ListInstancesPagedResponse listInstances(ProjectName parent) { /** * Lists all instances in the given project. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   String parent = ProjectName.of("[PROJECT]").toString();
    +   *   for (Instance element : instanceAdminClient.listInstances(parent).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param parent Required. The name of the project for which a list of instances is requested. * Values are of the form `projects/<project>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -342,6 +487,23 @@ public final ListInstancesPagedResponse listInstances(String parent) { /** * Lists all instances in the given project. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   ListInstancesRequest request =
    +   *       ListInstancesRequest.newBuilder()
    +   *           .setParent(ProjectName.of("[PROJECT]").toString())
    +   *           .setPageSize(883849137)
    +   *           .setPageToken("pageToken873572522")
    +   *           .setFilter("filter-1274492040")
    +   *           .build();
    +   *   for (Instance element : instanceAdminClient.listInstances(request).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -354,6 +516,24 @@ public final ListInstancesPagedResponse listInstances(ListInstancesRequest reque * Lists all instances in the given project. * *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   ListInstancesRequest request =
    +   *       ListInstancesRequest.newBuilder()
    +   *           .setParent(ProjectName.of("[PROJECT]").toString())
    +   *           .setPageSize(883849137)
    +   *           .setPageToken("pageToken873572522")
    +   *           .setFilter("filter-1274492040")
    +   *           .build();
    +   *   ApiFuture future =
    +   *       instanceAdminClient.listInstancesPagedCallable().futureCall(request);
    +   *   // Do something.
    +   *   for (Instance element : future.get().iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    */ public final UnaryCallable listInstancesPagedCallable() { @@ -365,6 +545,23 @@ public final ListInstancesPagedResponse listInstances(ListInstancesRequest reque * Lists all instances in the given project. * *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   while (true) {
    +   *     ListInstancesResponse response = instanceAdminClient.listInstancesCallable().call(request);
    +   *     for (Instance element : response.getResponsesList()) {
    +   *       // doThingsWith(element);
    +   *     }
    +   *     String nextPageToken = response.getNextPageToken();
    +   *     if (!Strings.isNullOrEmpty(nextPageToken)) {
    +   *       request = request.toBuilder().setPageToken(nextPageToken).build();
    +   *     } else {
    +   *       break;
    +   *     }
    +   *   }
    +   * }
    +   * }
    */ public final UnaryCallable listInstancesCallable() { return stub.listInstancesCallable(); @@ -374,6 +571,15 @@ public final UnaryCallable listInst /** * Gets information about a particular instance. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   InstanceName name = InstanceName.of("[PROJECT]", "[INSTANCE]");
    +   *   Instance response = instanceAdminClient.getInstance(name);
    +   * }
    +   * }
    + * * @param name Required. The name of the requested instance. Values are of the form * `projects/<project>/instances/<instance>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -388,6 +594,15 @@ public final Instance getInstance(InstanceName name) { /** * Gets information about a particular instance. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   String name = InstanceName.of("[PROJECT]", "[INSTANCE]").toString();
    +   *   Instance response = instanceAdminClient.getInstance(name);
    +   * }
    +   * }
    + * * @param name Required. The name of the requested instance. Values are of the form * `projects/<project>/instances/<instance>`. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -401,6 +616,19 @@ public final Instance getInstance(String name) { /** * Gets information about a particular instance. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   GetInstanceRequest request =
    +   *       GetInstanceRequest.newBuilder()
    +   *           .setName(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setFieldMask(FieldMask.newBuilder().build())
    +   *           .build();
    +   *   Instance response = instanceAdminClient.getInstance(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -413,6 +641,19 @@ public final Instance getInstance(GetInstanceRequest request) { * Gets information about a particular instance. * *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   GetInstanceRequest request =
    +   *       GetInstanceRequest.newBuilder()
    +   *           .setName(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .setFieldMask(FieldMask.newBuilder().build())
    +   *           .build();
    +   *   ApiFuture future = instanceAdminClient.getInstanceCallable().futureCall(request);
    +   *   // Do something.
    +   *   Instance response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable getInstanceCallable() { return stub.getInstanceCallable(); @@ -450,6 +691,18 @@ public final UnaryCallable getInstanceCallable() { * [response][google.longrunning.Operation.response] field type is * [Instance][google.spanner.admin.instance.v1.Instance], if successful. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   ProjectName parent = ProjectName.of("[PROJECT]");
    +   *   String instanceId = "instanceId902024336";
    +   *   Instance instance = Instance.newBuilder().build();
    +   *   Instance response =
    +   *       instanceAdminClient.createInstanceAsync(parent, instanceId, instance).get();
    +   * }
    +   * }
    + * * @param parent Required. The name of the project in which to create the instance. Values are of * the form `projects/<project>`. * @param instanceId Required. The ID of the instance to create. Valid identifiers are of the form @@ -501,6 +754,18 @@ public final OperationFuture createInstanceAsy * [response][google.longrunning.Operation.response] field type is * [Instance][google.spanner.admin.instance.v1.Instance], if successful. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   String parent = ProjectName.of("[PROJECT]").toString();
    +   *   String instanceId = "instanceId902024336";
    +   *   Instance instance = Instance.newBuilder().build();
    +   *   Instance response =
    +   *       instanceAdminClient.createInstanceAsync(parent, instanceId, instance).get();
    +   * }
    +   * }
    + * * @param parent Required. The name of the project in which to create the instance. Values are of * the form `projects/<project>`. * @param instanceId Required. The ID of the instance to create. Valid identifiers are of the form @@ -552,6 +817,20 @@ public final OperationFuture createInstanceAsy * [response][google.longrunning.Operation.response] field type is * [Instance][google.spanner.admin.instance.v1.Instance], if successful. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   CreateInstanceRequest request =
    +   *       CreateInstanceRequest.newBuilder()
    +   *           .setParent(ProjectName.of("[PROJECT]").toString())
    +   *           .setInstanceId("instanceId902024336")
    +   *           .setInstance(Instance.newBuilder().build())
    +   *           .build();
    +   *   Instance response = instanceAdminClient.createInstanceAsync(request).get();
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -593,6 +872,21 @@ public final OperationFuture createInstanceAsy * [Instance][google.spanner.admin.instance.v1.Instance], if successful. * *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   CreateInstanceRequest request =
    +   *       CreateInstanceRequest.newBuilder()
    +   *           .setParent(ProjectName.of("[PROJECT]").toString())
    +   *           .setInstanceId("instanceId902024336")
    +   *           .setInstance(Instance.newBuilder().build())
    +   *           .build();
    +   *   OperationFuture future =
    +   *       instanceAdminClient.createInstanceOperationCallable().futureCall(request);
    +   *   // Do something.
    +   *   Instance response = future.get();
    +   * }
    +   * }
    */ public final OperationCallable createInstanceOperationCallable() { @@ -632,6 +926,21 @@ public final OperationFuture createInstanceAsy * [Instance][google.spanner.admin.instance.v1.Instance], if successful. * *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   CreateInstanceRequest request =
    +   *       CreateInstanceRequest.newBuilder()
    +   *           .setParent(ProjectName.of("[PROJECT]").toString())
    +   *           .setInstanceId("instanceId902024336")
    +   *           .setInstance(Instance.newBuilder().build())
    +   *           .build();
    +   *   ApiFuture future =
    +   *       instanceAdminClient.createInstanceCallable().futureCall(request);
    +   *   // Do something.
    +   *   Operation response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable createInstanceCallable() { return stub.createInstanceCallable(); @@ -673,6 +982,16 @@ public final UnaryCallable createInstanceCalla *

    Authorization requires `spanner.instances.update` permission on resource * [name][google.spanner.admin.instance.v1.Instance.name]. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   Instance instance = Instance.newBuilder().build();
    +   *   FieldMask fieldMask = FieldMask.newBuilder().build();
    +   *   Instance response = instanceAdminClient.updateInstanceAsync(instance, fieldMask).get();
    +   * }
    +   * }
    + * * @param instance Required. The instance to update, which must always include the instance name. * Otherwise, only fields mentioned in * [field_mask][google.spanner.admin.instance.v1.UpdateInstanceRequest.field_mask] need be @@ -727,6 +1046,19 @@ public final OperationFuture updateInstanceAsy *

    Authorization requires `spanner.instances.update` permission on resource * [name][google.spanner.admin.instance.v1.Instance.name]. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   UpdateInstanceRequest request =
    +   *       UpdateInstanceRequest.newBuilder()
    +   *           .setInstance(Instance.newBuilder().build())
    +   *           .setFieldMask(FieldMask.newBuilder().build())
    +   *           .build();
    +   *   Instance response = instanceAdminClient.updateInstanceAsync(request).get();
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -772,6 +1104,20 @@ public final OperationFuture updateInstanceAsy * [name][google.spanner.admin.instance.v1.Instance.name]. * *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   UpdateInstanceRequest request =
    +   *       UpdateInstanceRequest.newBuilder()
    +   *           .setInstance(Instance.newBuilder().build())
    +   *           .setFieldMask(FieldMask.newBuilder().build())
    +   *           .build();
    +   *   OperationFuture future =
    +   *       instanceAdminClient.updateInstanceOperationCallable().futureCall(request);
    +   *   // Do something.
    +   *   Instance response = future.get();
    +   * }
    +   * }
    */ public final OperationCallable updateInstanceOperationCallable() { @@ -815,6 +1161,20 @@ public final OperationFuture updateInstanceAsy * [name][google.spanner.admin.instance.v1.Instance.name]. * *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   UpdateInstanceRequest request =
    +   *       UpdateInstanceRequest.newBuilder()
    +   *           .setInstance(Instance.newBuilder().build())
    +   *           .setFieldMask(FieldMask.newBuilder().build())
    +   *           .build();
    +   *   ApiFuture future =
    +   *       instanceAdminClient.updateInstanceCallable().futureCall(request);
    +   *   // Do something.
    +   *   Operation response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable updateInstanceCallable() { return stub.updateInstanceCallable(); @@ -833,6 +1193,15 @@ public final UnaryCallable updateInstanceCalla *

    * The instance and *all of its databases* immediately and irrevocably disappear * from the API. All data in the databases is permanently deleted. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   InstanceName name = InstanceName.of("[PROJECT]", "[INSTANCE]");
    +   *   instanceAdminClient.deleteInstance(name);
    +   * }
    +   * }
    + * * @param name Required. The name of the instance to be deleted. Values are of the form * `projects/<project>/instances/<instance>` * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -856,6 +1225,15 @@ public final void deleteInstance(InstanceName name) { *

    * The instance and *all of its databases* immediately and irrevocably disappear * from the API. All data in the databases is permanently deleted. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   String name = InstanceName.of("[PROJECT]", "[INSTANCE]").toString();
    +   *   instanceAdminClient.deleteInstance(name);
    +   * }
    +   * }
    + * * @param name Required. The name of the instance to be deleted. Values are of the form * `projects/<project>/instances/<instance>` * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -878,6 +1256,18 @@ public final void deleteInstance(String name) { *

    * The instance and *all of its databases* immediately and irrevocably disappear * from the API. All data in the databases is permanently deleted. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   DeleteInstanceRequest request =
    +   *       DeleteInstanceRequest.newBuilder()
    +   *           .setName(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .build();
    +   *   instanceAdminClient.deleteInstance(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -899,6 +1289,18 @@ public final void deleteInstance(DeleteInstanceRequest request) { * from the API. All data in the databases is permanently deleted. * *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   DeleteInstanceRequest request =
    +   *       DeleteInstanceRequest.newBuilder()
    +   *           .setName(InstanceName.of("[PROJECT]", "[INSTANCE]").toString())
    +   *           .build();
    +   *   ApiFuture future = instanceAdminClient.deleteInstanceCallable().futureCall(request);
    +   *   // Do something.
    +   *   future.get();
    +   * }
    +   * }
    */ public final UnaryCallable deleteInstanceCallable() { return stub.deleteInstanceCallable(); @@ -911,6 +1313,16 @@ public final UnaryCallable deleteInstanceCallable( *

    Authorization requires `spanner.instances.setIamPolicy` on * [resource][google.iam.v1.SetIamPolicyRequest.resource]. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   ResourceName resource = ProjectName.of("[PROJECT]");
    +   *   Policy policy = Policy.newBuilder().build();
    +   *   Policy response = instanceAdminClient.setIamPolicy(resource, policy);
    +   * }
    +   * }
    + * * @param resource REQUIRED: The resource for which the policy is being specified. See the * operation documentation for the appropriate value for this field. * @param policy REQUIRED: The complete policy to be applied to the `resource`. The size of the @@ -934,6 +1346,16 @@ public final Policy setIamPolicy(ResourceName resource, Policy policy) { *

    Authorization requires `spanner.instances.setIamPolicy` on * [resource][google.iam.v1.SetIamPolicyRequest.resource]. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   String resource = ProjectName.of("[PROJECT]").toString();
    +   *   Policy policy = Policy.newBuilder().build();
    +   *   Policy response = instanceAdminClient.setIamPolicy(resource, policy);
    +   * }
    +   * }
    + * * @param resource REQUIRED: The resource for which the policy is being specified. See the * operation documentation for the appropriate value for this field. * @param policy REQUIRED: The complete policy to be applied to the `resource`. The size of the @@ -954,6 +1376,19 @@ public final Policy setIamPolicy(String resource, Policy policy) { *

    Authorization requires `spanner.instances.setIamPolicy` on * [resource][google.iam.v1.SetIamPolicyRequest.resource]. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   SetIamPolicyRequest request =
    +   *       SetIamPolicyRequest.newBuilder()
    +   *           .setResource(ProjectName.of("[PROJECT]").toString())
    +   *           .setPolicy(Policy.newBuilder().build())
    +   *           .build();
    +   *   Policy response = instanceAdminClient.setIamPolicy(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -969,6 +1404,19 @@ public final Policy setIamPolicy(SetIamPolicyRequest request) { * [resource][google.iam.v1.SetIamPolicyRequest.resource]. * *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   SetIamPolicyRequest request =
    +   *       SetIamPolicyRequest.newBuilder()
    +   *           .setResource(ProjectName.of("[PROJECT]").toString())
    +   *           .setPolicy(Policy.newBuilder().build())
    +   *           .build();
    +   *   ApiFuture future = instanceAdminClient.setIamPolicyCallable().futureCall(request);
    +   *   // Do something.
    +   *   Policy response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable setIamPolicyCallable() { return stub.setIamPolicyCallable(); @@ -982,6 +1430,15 @@ public final UnaryCallable setIamPolicyCallable() { *

    Authorization requires `spanner.instances.getIamPolicy` on * [resource][google.iam.v1.GetIamPolicyRequest.resource]. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   ResourceName resource = ProjectName.of("[PROJECT]");
    +   *   Policy response = instanceAdminClient.getIamPolicy(resource);
    +   * }
    +   * }
    + * * @param resource REQUIRED: The resource for which the policy is being requested. See the * operation documentation for the appropriate value for this field. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -1002,6 +1459,15 @@ public final Policy getIamPolicy(ResourceName resource) { *

    Authorization requires `spanner.instances.getIamPolicy` on * [resource][google.iam.v1.GetIamPolicyRequest.resource]. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   String resource = ProjectName.of("[PROJECT]").toString();
    +   *   Policy response = instanceAdminClient.getIamPolicy(resource);
    +   * }
    +   * }
    + * * @param resource REQUIRED: The resource for which the policy is being requested. See the * operation documentation for the appropriate value for this field. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -1019,6 +1485,19 @@ public final Policy getIamPolicy(String resource) { *

    Authorization requires `spanner.instances.getIamPolicy` on * [resource][google.iam.v1.GetIamPolicyRequest.resource]. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   GetIamPolicyRequest request =
    +   *       GetIamPolicyRequest.newBuilder()
    +   *           .setResource(ProjectName.of("[PROJECT]").toString())
    +   *           .setOptions(GetPolicyOptions.newBuilder().build())
    +   *           .build();
    +   *   Policy response = instanceAdminClient.getIamPolicy(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -1035,6 +1514,19 @@ public final Policy getIamPolicy(GetIamPolicyRequest request) { * [resource][google.iam.v1.GetIamPolicyRequest.resource]. * *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   GetIamPolicyRequest request =
    +   *       GetIamPolicyRequest.newBuilder()
    +   *           .setResource(ProjectName.of("[PROJECT]").toString())
    +   *           .setOptions(GetPolicyOptions.newBuilder().build())
    +   *           .build();
    +   *   ApiFuture future = instanceAdminClient.getIamPolicyCallable().futureCall(request);
    +   *   // Do something.
    +   *   Policy response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable getIamPolicyCallable() { return stub.getIamPolicyCallable(); @@ -1048,6 +1540,17 @@ public final UnaryCallable getIamPolicyCallable() { * NOT_FOUND error if the user has `spanner.instances.list` permission on the containing Google * Cloud Project. Otherwise returns an empty set of permissions. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   ResourceName resource = ProjectName.of("[PROJECT]");
    +   *   List permissions = new ArrayList<>();
    +   *   TestIamPermissionsResponse response =
    +   *       instanceAdminClient.testIamPermissions(resource, permissions);
    +   * }
    +   * }
    + * * @param resource REQUIRED: The resource for which the policy detail is being requested. See the * operation documentation for the appropriate value for this field. * @param permissions The set of permissions to check for the `resource`. Permissions with @@ -1073,6 +1576,17 @@ public final TestIamPermissionsResponse testIamPermissions( * NOT_FOUND error if the user has `spanner.instances.list` permission on the containing Google * Cloud Project. Otherwise returns an empty set of permissions. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   String resource = ProjectName.of("[PROJECT]").toString();
    +   *   List permissions = new ArrayList<>();
    +   *   TestIamPermissionsResponse response =
    +   *       instanceAdminClient.testIamPermissions(resource, permissions);
    +   * }
    +   * }
    + * * @param resource REQUIRED: The resource for which the policy detail is being requested. See the * operation documentation for the appropriate value for this field. * @param permissions The set of permissions to check for the `resource`. Permissions with @@ -1098,6 +1612,19 @@ public final TestIamPermissionsResponse testIamPermissions( * NOT_FOUND error if the user has `spanner.instances.list` permission on the containing Google * Cloud Project. Otherwise returns an empty set of permissions. * + *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   TestIamPermissionsRequest request =
    +   *       TestIamPermissionsRequest.newBuilder()
    +   *           .setResource(ProjectName.of("[PROJECT]").toString())
    +   *           .addAllPermissions(new ArrayList())
    +   *           .build();
    +   *   TestIamPermissionsResponse response = instanceAdminClient.testIamPermissions(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -1114,6 +1641,20 @@ public final TestIamPermissionsResponse testIamPermissions(TestIamPermissionsReq * Cloud Project. Otherwise returns an empty set of permissions. * *

    Sample code: + * + *

    {@code
    +   * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    +   *   TestIamPermissionsRequest request =
    +   *       TestIamPermissionsRequest.newBuilder()
    +   *           .setResource(ProjectName.of("[PROJECT]").toString())
    +   *           .addAllPermissions(new ArrayList())
    +   *           .build();
    +   *   ApiFuture future =
    +   *       instanceAdminClient.testIamPermissionsCallable().futureCall(request);
    +   *   // Do something.
    +   *   TestIamPermissionsResponse response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable testIamPermissionsCallable() { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/package-info.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/package-info.java index 189e53a2d60..19b1d13aca7 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/package-info.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/package-info.java @@ -38,6 +38,13 @@ * databases in that instance, and their performance may suffer. * *

    Sample for InstanceAdminClient: + * + *

    {@code
    + * try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
    + *   InstanceConfigName name = InstanceConfigName.of("[PROJECT]", "[INSTANCE_CONFIG]");
    + *   InstanceConfig response = instanceAdminClient.getInstanceConfig(name);
    + * }
    + * }
    */ @Generated("by gapic-generator-java") package com.google.cloud.spanner.admin.instance.v1; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/SpannerClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/SpannerClient.java index 2c430faaade..c05c3d5ee9e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/SpannerClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/SpannerClient.java @@ -73,6 +73,13 @@ *

    This class provides the ability to make remote calls to the backing service through method * calls that map to API methods. Sample code to get started: * + *

    {@code
    + * try (SpannerClient spannerClient = SpannerClient.create()) {
    + *   DatabaseName database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
    + *   Session response = spannerClient.createSession(database);
    + * }
    + * }
    + * *

    Note: close() needs to be called on the SpannerClient object to clean up resources such as * threads. In the example above, try-with-resources is used, which automatically calls close(). * @@ -187,6 +194,15 @@ public SpannerStub getStub() { *

    Idle sessions can be kept alive by sending a trivial SQL query periodically, e.g., `"SELECT * 1"`. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   DatabaseName database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
    +   *   Session response = spannerClient.createSession(database);
    +   * }
    +   * }
    + * * @param database Required. The database in which the new session is created. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -216,6 +232,15 @@ public final Session createSession(DatabaseName database) { *

    Idle sessions can be kept alive by sending a trivial SQL query periodically, e.g., `"SELECT * 1"`. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   String database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString();
    +   *   Session response = spannerClient.createSession(database);
    +   * }
    +   * }
    + * * @param database Required. The database in which the new session is created. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -242,6 +267,19 @@ public final Session createSession(String database) { *

    Idle sessions can be kept alive by sending a trivial SQL query periodically, e.g., `"SELECT * 1"`. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   CreateSessionRequest request =
    +   *       CreateSessionRequest.newBuilder()
    +   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .setSession(Session.newBuilder().build())
    +   *           .build();
    +   *   Session response = spannerClient.createSession(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -268,6 +306,19 @@ public final Session createSession(CreateSessionRequest request) { * 1"`. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   CreateSessionRequest request =
    +   *       CreateSessionRequest.newBuilder()
    +   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .setSession(Session.newBuilder().build())
    +   *           .build();
    +   *   ApiFuture future = spannerClient.createSessionCallable().futureCall(request);
    +   *   // Do something.
    +   *   Session response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable createSessionCallable() { return stub.createSessionCallable(); @@ -280,6 +331,17 @@ public final UnaryCallable createSessionCallable( *

    This API can be used to initialize a session cache on the clients. See https://goo.gl/TgSFN2 * for best practices on session cache management. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   DatabaseName database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
    +   *   int sessionCount = 185691686;
    +   *   BatchCreateSessionsResponse response =
    +   *       spannerClient.batchCreateSessions(database, sessionCount);
    +   * }
    +   * }
    + * * @param database Required. The database in which the new sessions are created. * @param sessionCount Required. The number of sessions to be created in this batch call. The API * may return fewer than the requested number of sessions. If a specific number of sessions @@ -304,6 +366,17 @@ public final BatchCreateSessionsResponse batchCreateSessions( *

    This API can be used to initialize a session cache on the clients. See https://goo.gl/TgSFN2 * for best practices on session cache management. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   String database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString();
    +   *   int sessionCount = 185691686;
    +   *   BatchCreateSessionsResponse response =
    +   *       spannerClient.batchCreateSessions(database, sessionCount);
    +   * }
    +   * }
    + * * @param database Required. The database in which the new sessions are created. * @param sessionCount Required. The number of sessions to be created in this batch call. The API * may return fewer than the requested number of sessions. If a specific number of sessions @@ -327,6 +400,20 @@ public final BatchCreateSessionsResponse batchCreateSessions(String database, in *

    This API can be used to initialize a session cache on the clients. See https://goo.gl/TgSFN2 * for best practices on session cache management. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   BatchCreateSessionsRequest request =
    +   *       BatchCreateSessionsRequest.newBuilder()
    +   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .setSessionTemplate(Session.newBuilder().build())
    +   *           .setSessionCount(185691686)
    +   *           .build();
    +   *   BatchCreateSessionsResponse response = spannerClient.batchCreateSessions(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -342,6 +429,21 @@ public final BatchCreateSessionsResponse batchCreateSessions(BatchCreateSessions * for best practices on session cache management. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   BatchCreateSessionsRequest request =
    +   *       BatchCreateSessionsRequest.newBuilder()
    +   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .setSessionTemplate(Session.newBuilder().build())
    +   *           .setSessionCount(185691686)
    +   *           .build();
    +   *   ApiFuture future =
    +   *       spannerClient.batchCreateSessionsCallable().futureCall(request);
    +   *   // Do something.
    +   *   BatchCreateSessionsResponse response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable batchCreateSessionsCallable() { @@ -353,6 +455,15 @@ public final BatchCreateSessionsResponse batchCreateSessions(BatchCreateSessions * Gets a session. Returns `NOT_FOUND` if the session does not exist. This is mainly useful for * determining whether a session is still alive. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   SessionName name = SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]");
    +   *   Session response = spannerClient.getSession(name);
    +   * }
    +   * }
    + * * @param name Required. The name of the session to retrieve. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -367,6 +478,15 @@ public final Session getSession(SessionName name) { * Gets a session. Returns `NOT_FOUND` if the session does not exist. This is mainly useful for * determining whether a session is still alive. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   String name = SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString();
    +   *   Session response = spannerClient.getSession(name);
    +   * }
    +   * }
    + * * @param name Required. The name of the session to retrieve. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -380,6 +500,19 @@ public final Session getSession(String name) { * Gets a session. Returns `NOT_FOUND` if the session does not exist. This is mainly useful for * determining whether a session is still alive. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   GetSessionRequest request =
    +   *       GetSessionRequest.newBuilder()
    +   *           .setName(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .build();
    +   *   Session response = spannerClient.getSession(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -393,6 +526,19 @@ public final Session getSession(GetSessionRequest request) { * determining whether a session is still alive. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   GetSessionRequest request =
    +   *       GetSessionRequest.newBuilder()
    +   *           .setName(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .build();
    +   *   ApiFuture future = spannerClient.getSessionCallable().futureCall(request);
    +   *   // Do something.
    +   *   Session response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable getSessionCallable() { return stub.getSessionCallable(); @@ -402,6 +548,17 @@ public final UnaryCallable getSessionCallable() { /** * Lists all sessions in a given database. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   DatabaseName database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
    +   *   for (Session element : spannerClient.listSessions(database).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param database Required. The database in which to list sessions. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -417,6 +574,17 @@ public final ListSessionsPagedResponse listSessions(DatabaseName database) { /** * Lists all sessions in a given database. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   String database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString();
    +   *   for (Session element : spannerClient.listSessions(database).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param database Required. The database in which to list sessions. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -429,6 +597,23 @@ public final ListSessionsPagedResponse listSessions(String database) { /** * Lists all sessions in a given database. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   ListSessionsRequest request =
    +   *       ListSessionsRequest.newBuilder()
    +   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .setPageSize(883849137)
    +   *           .setPageToken("pageToken873572522")
    +   *           .setFilter("filter-1274492040")
    +   *           .build();
    +   *   for (Session element : spannerClient.listSessions(request).iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -441,6 +626,23 @@ public final ListSessionsPagedResponse listSessions(ListSessionsRequest request) * Lists all sessions in a given database. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   ListSessionsRequest request =
    +   *       ListSessionsRequest.newBuilder()
    +   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
    +   *           .setPageSize(883849137)
    +   *           .setPageToken("pageToken873572522")
    +   *           .setFilter("filter-1274492040")
    +   *           .build();
    +   *   ApiFuture future = spannerClient.listSessionsPagedCallable().futureCall(request);
    +   *   // Do something.
    +   *   for (Session element : future.get().iterateAll()) {
    +   *     // doThingsWith(element);
    +   *   }
    +   * }
    +   * }
    */ public final UnaryCallable listSessionsPagedCallable() { @@ -452,6 +654,23 @@ public final ListSessionsPagedResponse listSessions(ListSessionsRequest request) * Lists all sessions in a given database. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   while (true) {
    +   *     ListSessionsResponse response = spannerClient.listSessionsCallable().call(request);
    +   *     for (Session element : response.getResponsesList()) {
    +   *       // doThingsWith(element);
    +   *     }
    +   *     String nextPageToken = response.getNextPageToken();
    +   *     if (!Strings.isNullOrEmpty(nextPageToken)) {
    +   *       request = request.toBuilder().setPageToken(nextPageToken).build();
    +   *     } else {
    +   *       break;
    +   *     }
    +   *   }
    +   * }
    +   * }
    */ public final UnaryCallable listSessionsCallable() { return stub.listSessionsCallable(); @@ -462,6 +681,15 @@ public final UnaryCallable listSessio * Ends a session, releasing server resources associated with it. This will asynchronously trigger * cancellation of any operations that are running with this session. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   SessionName name = SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]");
    +   *   spannerClient.deleteSession(name);
    +   * }
    +   * }
    + * * @param name Required. The name of the session to delete. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -476,6 +704,15 @@ public final void deleteSession(SessionName name) { * Ends a session, releasing server resources associated with it. This will asynchronously trigger * cancellation of any operations that are running with this session. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   String name = SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString();
    +   *   spannerClient.deleteSession(name);
    +   * }
    +   * }
    + * * @param name Required. The name of the session to delete. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -489,6 +726,19 @@ public final void deleteSession(String name) { * Ends a session, releasing server resources associated with it. This will asynchronously trigger * cancellation of any operations that are running with this session. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   DeleteSessionRequest request =
    +   *       DeleteSessionRequest.newBuilder()
    +   *           .setName(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .build();
    +   *   spannerClient.deleteSession(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -502,6 +752,19 @@ public final void deleteSession(DeleteSessionRequest request) { * cancellation of any operations that are running with this session. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   DeleteSessionRequest request =
    +   *       DeleteSessionRequest.newBuilder()
    +   *           .setName(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .build();
    +   *   ApiFuture future = spannerClient.deleteSessionCallable().futureCall(request);
    +   *   // Do something.
    +   *   future.get();
    +   * }
    +   * }
    */ public final UnaryCallable deleteSessionCallable() { return stub.deleteSessionCallable(); @@ -520,6 +783,27 @@ public final UnaryCallable deleteSessionCallable() *

    Larger result sets can be fetched in streaming fashion by calling * [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql] instead. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   ExecuteSqlRequest request =
    +   *       ExecuteSqlRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setTransaction(TransactionSelector.newBuilder().build())
    +   *           .setSql("sql114126")
    +   *           .setParams(Struct.newBuilder().build())
    +   *           .putAllParamTypes(new HashMap())
    +   *           .setResumeToken(ByteString.EMPTY)
    +   *           .setPartitionToken(ByteString.EMPTY)
    +   *           .setSeqno(109325920)
    +   *           .setQueryOptions(ExecuteSqlRequest.QueryOptions.newBuilder().build())
    +   *           .build();
    +   *   ResultSet response = spannerClient.executeSql(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -541,6 +825,27 @@ public final ResultSet executeSql(ExecuteSqlRequest request) { * [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql] instead. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   ExecuteSqlRequest request =
    +   *       ExecuteSqlRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setTransaction(TransactionSelector.newBuilder().build())
    +   *           .setSql("sql114126")
    +   *           .setParams(Struct.newBuilder().build())
    +   *           .putAllParamTypes(new HashMap())
    +   *           .setResumeToken(ByteString.EMPTY)
    +   *           .setPartitionToken(ByteString.EMPTY)
    +   *           .setSeqno(109325920)
    +   *           .setQueryOptions(ExecuteSqlRequest.QueryOptions.newBuilder().build())
    +   *           .build();
    +   *   ApiFuture future = spannerClient.executeSqlCallable().futureCall(request);
    +   *   // Do something.
    +   *   ResultSet response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable executeSqlCallable() { return stub.executeSqlCallable(); @@ -554,6 +859,29 @@ public final UnaryCallable executeSqlCallable() { * MiB, and no column value can exceed 10 MiB. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   ExecuteSqlRequest request =
    +   *       ExecuteSqlRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setTransaction(TransactionSelector.newBuilder().build())
    +   *           .setSql("sql114126")
    +   *           .setParams(Struct.newBuilder().build())
    +   *           .putAllParamTypes(new HashMap())
    +   *           .setResumeToken(ByteString.EMPTY)
    +   *           .setPartitionToken(ByteString.EMPTY)
    +   *           .setSeqno(109325920)
    +   *           .setQueryOptions(ExecuteSqlRequest.QueryOptions.newBuilder().build())
    +   *           .build();
    +   *   ServerStream stream =
    +   *       spannerClient.executeStreamingSqlCallable().call(request);
    +   *   for (PartialResultSet response : stream) {
    +   *     // Do something when a response is received.
    +   *   }
    +   * }
    +   * }
    */ public final ServerStreamingCallable executeStreamingSqlCallable() { @@ -573,6 +901,22 @@ public final UnaryCallable executeSqlCallable() { * *

    Execution stops after the first failed statement; the remaining statements are not executed. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   ExecuteBatchDmlRequest request =
    +   *       ExecuteBatchDmlRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setTransaction(TransactionSelector.newBuilder().build())
    +   *           .addAllStatements(new ArrayList())
    +   *           .setSeqno(109325920)
    +   *           .build();
    +   *   ExecuteBatchDmlResponse response = spannerClient.executeBatchDml(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -594,6 +938,23 @@ public final ExecuteBatchDmlResponse executeBatchDml(ExecuteBatchDmlRequest requ *

    Execution stops after the first failed statement; the remaining statements are not executed. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   ExecuteBatchDmlRequest request =
    +   *       ExecuteBatchDmlRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setTransaction(TransactionSelector.newBuilder().build())
    +   *           .addAllStatements(new ArrayList())
    +   *           .setSeqno(109325920)
    +   *           .build();
    +   *   ApiFuture future =
    +   *       spannerClient.executeBatchDmlCallable().futureCall(request);
    +   *   // Do something.
    +   *   ExecuteBatchDmlResponse response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable executeBatchDmlCallable() { @@ -614,6 +975,27 @@ public final ExecuteBatchDmlResponse executeBatchDml(ExecuteBatchDmlRequest requ *

    Larger result sets can be yielded in streaming fashion by calling * [StreamingRead][google.spanner.v1.Spanner.StreamingRead] instead. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   ReadRequest request =
    +   *       ReadRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setTransaction(TransactionSelector.newBuilder().build())
    +   *           .setTable("table110115790")
    +   *           .setIndex("index100346066")
    +   *           .addAllColumns(new ArrayList())
    +   *           .setKeySet(KeySet.newBuilder().build())
    +   *           .setLimit(102976443)
    +   *           .setResumeToken(ByteString.EMPTY)
    +   *           .setPartitionToken(ByteString.EMPTY)
    +   *           .build();
    +   *   ResultSet response = spannerClient.read(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -636,6 +1018,27 @@ public final ResultSet read(ReadRequest request) { * [StreamingRead][google.spanner.v1.Spanner.StreamingRead] instead. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   ReadRequest request =
    +   *       ReadRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setTransaction(TransactionSelector.newBuilder().build())
    +   *           .setTable("table110115790")
    +   *           .setIndex("index100346066")
    +   *           .addAllColumns(new ArrayList())
    +   *           .setKeySet(KeySet.newBuilder().build())
    +   *           .setLimit(102976443)
    +   *           .setResumeToken(ByteString.EMPTY)
    +   *           .setPartitionToken(ByteString.EMPTY)
    +   *           .build();
    +   *   ApiFuture future = spannerClient.readCallable().futureCall(request);
    +   *   // Do something.
    +   *   ResultSet response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable readCallable() { return stub.readCallable(); @@ -649,6 +1052,28 @@ public final UnaryCallable readCallable() { * exceed 10 MiB. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   ReadRequest request =
    +   *       ReadRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setTransaction(TransactionSelector.newBuilder().build())
    +   *           .setTable("table110115790")
    +   *           .setIndex("index100346066")
    +   *           .addAllColumns(new ArrayList())
    +   *           .setKeySet(KeySet.newBuilder().build())
    +   *           .setLimit(102976443)
    +   *           .setResumeToken(ByteString.EMPTY)
    +   *           .setPartitionToken(ByteString.EMPTY)
    +   *           .build();
    +   *   ServerStream stream = spannerClient.streamingReadCallable().call(request);
    +   *   for (PartialResultSet response : stream) {
    +   *     // Do something when a response is received.
    +   *   }
    +   * }
    +   * }
    */ public final ServerStreamingCallable streamingReadCallable() { return stub.streamingReadCallable(); @@ -660,6 +1085,16 @@ public final ServerStreamingCallable streamingRea * [Read][google.spanner.v1.Spanner.Read], [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql] and * [Commit][google.spanner.v1.Spanner.Commit] can begin a new transaction as a side-effect. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   SessionName session = SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]");
    +   *   TransactionOptions options = TransactionOptions.newBuilder().build();
    +   *   Transaction response = spannerClient.beginTransaction(session, options);
    +   * }
    +   * }
    + * * @param session Required. The session in which the transaction runs. * @param options Required. Options for the new transaction. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -679,6 +1114,17 @@ public final Transaction beginTransaction(SessionName session, TransactionOption * [Read][google.spanner.v1.Spanner.Read], [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql] and * [Commit][google.spanner.v1.Spanner.Commit] can begin a new transaction as a side-effect. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   String session =
    +   *       SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString();
    +   *   TransactionOptions options = TransactionOptions.newBuilder().build();
    +   *   Transaction response = spannerClient.beginTransaction(session, options);
    +   * }
    +   * }
    + * * @param session Required. The session in which the transaction runs. * @param options Required. Options for the new transaction. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -695,6 +1141,20 @@ public final Transaction beginTransaction(String session, TransactionOptions opt * [Read][google.spanner.v1.Spanner.Read], [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql] and * [Commit][google.spanner.v1.Spanner.Commit] can begin a new transaction as a side-effect. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   BeginTransactionRequest request =
    +   *       BeginTransactionRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setOptions(TransactionOptions.newBuilder().build())
    +   *           .build();
    +   *   Transaction response = spannerClient.beginTransaction(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -709,6 +1169,20 @@ public final Transaction beginTransaction(BeginTransactionRequest request) { * [Commit][google.spanner.v1.Spanner.Commit] can begin a new transaction as a side-effect. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   BeginTransactionRequest request =
    +   *       BeginTransactionRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setOptions(TransactionOptions.newBuilder().build())
    +   *           .build();
    +   *   ApiFuture future = spannerClient.beginTransactionCallable().futureCall(request);
    +   *   // Do something.
    +   *   Transaction response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable beginTransactionCallable() { return stub.beginTransactionCallable(); @@ -729,6 +1203,17 @@ public final UnaryCallable beginTransactio * track of the transaction outcome and we recommend that you perform another read from the * database to see the state of things as they are now. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   SessionName session = SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]");
    +   *   ByteString transactionId = ByteString.EMPTY;
    +   *   List mutations = new ArrayList<>();
    +   *   CommitResponse response = spannerClient.commit(session, transactionId, mutations);
    +   * }
    +   * }
    + * * @param session Required. The session in which the transaction to be committed is running. * @param transactionId Commit a previously-started transaction. * @param mutations The mutations to be executed when this transaction commits. All mutations are @@ -761,6 +1246,17 @@ public final CommitResponse commit( * track of the transaction outcome and we recommend that you perform another read from the * database to see the state of things as they are now. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   SessionName session = SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]");
    +   *   TransactionOptions singleUseTransaction = TransactionOptions.newBuilder().build();
    +   *   List mutations = new ArrayList<>();
    +   *   CommitResponse response = spannerClient.commit(session, singleUseTransaction, mutations);
    +   * }
    +   * }
    + * * @param session Required. The session in which the transaction to be committed is running. * @param singleUseTransaction Execute mutations in a temporary transaction. Note that unlike * commit of a previously-started transaction, commit with a temporary transaction is @@ -799,6 +1295,18 @@ public final CommitResponse commit( * track of the transaction outcome and we recommend that you perform another read from the * database to see the state of things as they are now. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   String session =
    +   *       SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString();
    +   *   ByteString transactionId = ByteString.EMPTY;
    +   *   List mutations = new ArrayList<>();
    +   *   CommitResponse response = spannerClient.commit(session, transactionId, mutations);
    +   * }
    +   * }
    + * * @param session Required. The session in which the transaction to be committed is running. * @param transactionId Commit a previously-started transaction. * @param mutations The mutations to be executed when this transaction commits. All mutations are @@ -831,6 +1339,18 @@ public final CommitResponse commit( * track of the transaction outcome and we recommend that you perform another read from the * database to see the state of things as they are now. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   String session =
    +   *       SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString();
    +   *   TransactionOptions singleUseTransaction = TransactionOptions.newBuilder().build();
    +   *   List mutations = new ArrayList<>();
    +   *   CommitResponse response = spannerClient.commit(session, singleUseTransaction, mutations);
    +   * }
    +   * }
    + * * @param session Required. The session in which the transaction to be committed is running. * @param singleUseTransaction Execute mutations in a temporary transaction. Note that unlike * commit of a previously-started transaction, commit with a temporary transaction is @@ -869,6 +1389,21 @@ public final CommitResponse commit( * track of the transaction outcome and we recommend that you perform another read from the * database to see the state of things as they are now. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   CommitRequest request =
    +   *       CommitRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .addAllMutations(new ArrayList())
    +   *           .setReturnCommitStats(true)
    +   *           .build();
    +   *   CommitResponse response = spannerClient.commit(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -892,6 +1427,21 @@ public final CommitResponse commit(CommitRequest request) { * database to see the state of things as they are now. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   CommitRequest request =
    +   *       CommitRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .addAllMutations(new ArrayList())
    +   *           .setReturnCommitStats(true)
    +   *           .build();
    +   *   ApiFuture future = spannerClient.commitCallable().futureCall(request);
    +   *   // Do something.
    +   *   CommitResponse response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable commitCallable() { return stub.commitCallable(); @@ -907,6 +1457,16 @@ public final UnaryCallable commitCallable() { *

    `Rollback` returns `OK` if it successfully aborts the transaction, the transaction was * already aborted, or the transaction is not found. `Rollback` never returns `ABORTED`. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   SessionName session = SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]");
    +   *   ByteString transactionId = ByteString.EMPTY;
    +   *   spannerClient.rollback(session, transactionId);
    +   * }
    +   * }
    + * * @param session Required. The session in which the transaction to roll back is running. * @param transactionId Required. The transaction to roll back. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -930,6 +1490,17 @@ public final void rollback(SessionName session, ByteString transactionId) { *

    `Rollback` returns `OK` if it successfully aborts the transaction, the transaction was * already aborted, or the transaction is not found. `Rollback` never returns `ABORTED`. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   String session =
    +   *       SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString();
    +   *   ByteString transactionId = ByteString.EMPTY;
    +   *   spannerClient.rollback(session, transactionId);
    +   * }
    +   * }
    + * * @param session Required. The session in which the transaction to roll back is running. * @param transactionId Required. The transaction to roll back. * @throws com.google.api.gax.rpc.ApiException if the remote call fails @@ -950,6 +1521,20 @@ public final void rollback(String session, ByteString transactionId) { *

    `Rollback` returns `OK` if it successfully aborts the transaction, the transaction was * already aborted, or the transaction is not found. `Rollback` never returns `ABORTED`. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   RollbackRequest request =
    +   *       RollbackRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setTransactionId(ByteString.EMPTY)
    +   *           .build();
    +   *   spannerClient.rollback(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -968,6 +1553,20 @@ public final void rollback(RollbackRequest request) { * already aborted, or the transaction is not found. `Rollback` never returns `ABORTED`. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   RollbackRequest request =
    +   *       RollbackRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setTransactionId(ByteString.EMPTY)
    +   *           .build();
    +   *   ApiFuture future = spannerClient.rollbackCallable().futureCall(request);
    +   *   // Do something.
    +   *   future.get();
    +   * }
    +   * }
    */ public final UnaryCallable rollbackCallable() { return stub.rollbackCallable(); @@ -986,6 +1585,24 @@ public final UnaryCallable rollbackCallable() { * too long, begins a new transaction, or becomes too old. When any of these happen, it is not * possible to resume the query, and the whole operation must be restarted from the beginning. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   PartitionQueryRequest request =
    +   *       PartitionQueryRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setTransaction(TransactionSelector.newBuilder().build())
    +   *           .setSql("sql114126")
    +   *           .setParams(Struct.newBuilder().build())
    +   *           .putAllParamTypes(new HashMap())
    +   *           .setPartitionOptions(PartitionOptions.newBuilder().build())
    +   *           .build();
    +   *   PartitionResponse response = spannerClient.partitionQuery(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -1007,6 +1624,25 @@ public final PartitionResponse partitionQuery(PartitionQueryRequest request) { * possible to resume the query, and the whole operation must be restarted from the beginning. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   PartitionQueryRequest request =
    +   *       PartitionQueryRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setTransaction(TransactionSelector.newBuilder().build())
    +   *           .setSql("sql114126")
    +   *           .setParams(Struct.newBuilder().build())
    +   *           .putAllParamTypes(new HashMap())
    +   *           .setPartitionOptions(PartitionOptions.newBuilder().build())
    +   *           .build();
    +   *   ApiFuture future =
    +   *       spannerClient.partitionQueryCallable().futureCall(request);
    +   *   // Do something.
    +   *   PartitionResponse response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable partitionQueryCallable() { return stub.partitionQueryCallable(); @@ -1026,6 +1662,25 @@ public final UnaryCallable partitionQu * too long, begins a new transaction, or becomes too old. When any of these happen, it is not * possible to resume the read, and the whole operation must be restarted from the beginning. * + *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   PartitionReadRequest request =
    +   *       PartitionReadRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setTransaction(TransactionSelector.newBuilder().build())
    +   *           .setTable("table110115790")
    +   *           .setIndex("index100346066")
    +   *           .addAllColumns(new ArrayList())
    +   *           .setKeySet(KeySet.newBuilder().build())
    +   *           .setPartitionOptions(PartitionOptions.newBuilder().build())
    +   *           .build();
    +   *   PartitionResponse response = spannerClient.partitionRead(request);
    +   * }
    +   * }
    + * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ @@ -1048,6 +1703,26 @@ public final PartitionResponse partitionRead(PartitionReadRequest request) { * possible to resume the read, and the whole operation must be restarted from the beginning. * *

    Sample code: + * + *

    {@code
    +   * try (SpannerClient spannerClient = SpannerClient.create()) {
    +   *   PartitionReadRequest request =
    +   *       PartitionReadRequest.newBuilder()
    +   *           .setSession(
    +   *               SessionName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]").toString())
    +   *           .setTransaction(TransactionSelector.newBuilder().build())
    +   *           .setTable("table110115790")
    +   *           .setIndex("index100346066")
    +   *           .addAllColumns(new ArrayList())
    +   *           .setKeySet(KeySet.newBuilder().build())
    +   *           .setPartitionOptions(PartitionOptions.newBuilder().build())
    +   *           .build();
    +   *   ApiFuture future =
    +   *       spannerClient.partitionReadCallable().futureCall(request);
    +   *   // Do something.
    +   *   PartitionResponse response = future.get();
    +   * }
    +   * }
    */ public final UnaryCallable partitionReadCallable() { return stub.partitionReadCallable(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/package-info.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/package-info.java index 7e442cc8b47..0f340c9469a 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/package-info.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/package-info.java @@ -25,6 +25,13 @@ * in Cloud Spanner databases. * *

    Sample for SpannerClient: + * + *

    {@code
    + * try (SpannerClient spannerClient = SpannerClient.create()) {
    + *   DatabaseName database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
    + *   Session response = spannerClient.createSession(database);
    + * }
    + * }
    */ @Generated("by gapic-generator-java") package com.google.cloud.spanner.v1; diff --git a/synth.metadata b/synth.metadata index 7e28947f74e..ec08e646e16 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,15 +4,15 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "2887bbb77141ddc92d8ef79d6221a041b4330999" + "sha": "58cebd85a9d82bd1526b9eae98892181f1a022f1" } }, { "git": { "name": "googleapis", "remote": "https://github.com/googleapis/googleapis.git", - "sha": "30eb779ddcc4f389df34d0802d081ef6c4559d6d", - "internalRef": "354462069" + "sha": "8d8c008e56f1af31d57f75561e0f1848ffb29eeb", + "internalRef": "356341083" } }, { From a63924b088c54962bf963ab9e27fa92c8b090227 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Wed, 10 Feb 2021 09:00:13 -0800 Subject: [PATCH 31/42] build(java): update autorelease script (#863) This PR was generated using Autosynth. :rainbow: Synth log will be available here: https://source.cloud.google.com/results/invocations/d3b31218-a85c-4976-9854-6b0543a40737/targets - [ ] To automatically regenerate this PR, check this box. Source-Link: https://github.com/googleapis/synthtool/commit/2414b817065726eae0bc525346c7e874f969369d --- .github/workflows/auto-release.yaml | 4 ++-- synth.metadata | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/auto-release.yaml b/.github/workflows/auto-release.yaml index 76e6404b4e0..7c8816a7d7b 100644 --- a/.github/workflows/auto-release.yaml +++ b/.github/workflows/auto-release.yaml @@ -4,7 +4,7 @@ name: auto-release jobs: approve: runs-on: ubuntu-latest - if: contains(github.head_ref, 'release-v') + if: contains(github.head_ref, 'release-please') steps: - uses: actions/github-script@v3 with: @@ -16,7 +16,7 @@ jobs: return; } - // only approve PRs like "chore: release " + // only approve PRs like "chore(master): release " if ( !context.payload.pull_request.title.startsWith("chore(master): release") ) { return; } diff --git a/synth.metadata b/synth.metadata index ec08e646e16..d7cc91b54ba 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "58cebd85a9d82bd1526b9eae98892181f1a022f1" + "sha": "7cdfb82b40487600547d0bad92119508161ca689" } }, { @@ -19,7 +19,7 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "692715c0f23a7bb3bfbbaa300f7620ddfa8c47e5" + "sha": "2414b817065726eae0bc525346c7e874f969369d" } } ], From 54cfa0f70f9b117ff50310adc5e01282e4bd82ed Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Wed, 10 Feb 2021 09:38:07 -0800 Subject: [PATCH 32/42] chore: regenerate README (#864) This PR was generated using Autosynth. :rainbow:
    Log from Synthtool ``` 2021-02-10 17:02:29,334 synthtool [DEBUG] > Executing /root/.cache/synthtool/java-spanner/.github/readme/synth.py. On branch autosynth-readme nothing to commit, working tree clean 2021-02-10 17:02:30,304 synthtool [DEBUG] > Wrote metadata to .github/readme/synth.metadata/synth.metadata. ```
    Full log will be available here: https://source.cloud.google.com/results/invocations/fdf98902-6041-43f0-8695-ea336573ec53/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 54018735ad9..fb9390e2e28 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": "fadb4011e554d0554332ac47e1c0060afca9ffeb" + "sha": "a63924b088c54962bf963ab9e27fa92c8b090227" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "56ddc68f36b32341e9f22c2c59b4ce6aa3ba635f" + "sha": "2414b817065726eae0bc525346c7e874f969369d" } } ] diff --git a/README.md b/README.md index 1ddee68fa74..0fc145b29b7 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ If you are using Maven without 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:16.3.0') +implementation platform('com.google.cloud:libraries-bom:16.4.0') compile 'com.google.cloud:google-cloud-spanner' ``` From a36be4c02d26c29c6ee52c6dd5e5f40b3728f91d Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Mon, 15 Feb 2021 07:08:30 +0100 Subject: [PATCH 33/42] test(deps): update dependency junit:junit to v4.13.2 (#870) --- 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 48a162429e7..7a1418ae958 100644 --- a/pom.xml +++ b/pom.xml @@ -115,7 +115,7 @@ junit junit - 4.13.1 + 4.13.2 test diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index 5373c065550..b9c60d3097b 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -93,7 +93,7 @@ junit junit - 4.13.1 + 4.13.2 test diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index e66974d9002..5bbf704d5f1 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -92,7 +92,7 @@ junit junit - 4.13.1 + 4.13.2 test diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 8fbf7b1b9e2..5f5f19943e5 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -104,7 +104,7 @@ junit junit - 4.13.1 + 4.13.2 test From 5af36739532037360dfd504a4a0988562550526c Mon Sep 17 00:00:00 2001 From: Elliotte Rusty Harold Date: Tue, 16 Feb 2021 13:40:02 +0000 Subject: [PATCH 34/42] docs: libraries-bom 16.4.0 (#867) @suztomo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0fc145b29b7..f35503e755a 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 - 16.3.0 + 16.4.0 pom import From 2b2a964e0c3605950112a3c3a35e32dca09f9de3 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Tue, 16 Feb 2021 06:20:02 -0800 Subject: [PATCH 35/42] chore: regenerate README (#873) This PR was generated using Autosynth. :rainbow:
    Log from Synthtool ``` 2021-02-16 13:42:20,405 synthtool [DEBUG] > Executing /root/.cache/synthtool/java-spanner/.github/readme/synth.py. On branch autosynth-readme nothing to commit, working tree clean 2021-02-16 13:42:21,947 synthtool [DEBUG] > Wrote metadata to .github/readme/synth.metadata/synth.metadata. ```
    Full log will be available here: https://source.cloud.google.com/results/invocations/172c4d83-9ece-4a07-b9d5-dbbe07c16651/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 fb9390e2e28..d295adb59e8 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": "a63924b088c54962bf963ab9e27fa92c8b090227" + "sha": "5af36739532037360dfd504a4a0988562550526c" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "2414b817065726eae0bc525346c7e874f969369d" + "sha": "b32c87d2c087fb2a841acc623ab540105de821af" } } ] diff --git a/README.md b/README.md index f35503e755a..0fc145b29b7 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 - 16.4.0 + 16.3.0 pom import From 3f5351df6d13bd3bc3639e8a75e24ae924a4cfec Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 16 Feb 2021 15:54:28 +0100 Subject: [PATCH 36/42] chore(deps): update dependency com.google.cloud:libraries-bom to v16.4.0 (#861) --- 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 5f5f19943e5..642eec6ea62 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -33,7 +33,7 @@ com.google.cloud libraries-bom - 16.3.0 + 16.4.0 pom import From 326b8331d952005cf48eaa9efb054fab5918f7e9 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Tue, 16 Feb 2021 07:28:02 -0800 Subject: [PATCH 37/42] chore: regenerate README (#876) This PR was generated using Autosynth. :rainbow:
    Log from Synthtool ``` 2021-02-16 14:56:36,897 synthtool [DEBUG] > Executing /root/.cache/synthtool/java-spanner/.github/readme/synth.py. On branch autosynth-readme nothing to commit, working tree clean 2021-02-16 14:56:38,474 synthtool [DEBUG] > Wrote metadata to .github/readme/synth.metadata/synth.metadata. ```
    Full log will be available here: https://source.cloud.google.com/results/invocations/6601876f-4193-4e02-be31-8c69bfbabd58/targets - [ ] To automatically regenerate this PR, check this box. --- .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 d295adb59e8..dfef9855f54 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": "5af36739532037360dfd504a4a0988562550526c" + "sha": "3f5351df6d13bd3bc3639e8a75e24ae924a4cfec" } }, { diff --git a/README.md b/README.md index 0fc145b29b7..f35503e755a 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 - 16.3.0 + 16.4.0 pom import From 5e07e4e12f08acc6a1491f7aa67cd289c54136bd Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Tue, 16 Feb 2021 16:18:20 -0800 Subject: [PATCH 38/42] ci: linkage monitor CI job to use build.sh (#878) This PR was generated using Autosynth. :rainbow: Synth log will be available here: https://source.cloud.google.com/results/invocations/96c62e92-13ae-4f3c-9727-c854ec8b7488/targets - [ ] To automatically regenerate this PR, check this box. Source-Link: https://github.com/googleapis/synthtool/commit/f327d3b657a63ae4a8efd7f011a15eacae36b59c --- .github/workflows/ci.yaml | 13 +++---------- synth.metadata | 4 ++-- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 98a472e132d..def8b3a2c84 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -55,17 +55,10 @@ jobs: java-version: 8 - run: java -version - name: Install artifacts to local Maven repository - run: | - source .kokoro/common.sh - retry_with_backoff 3 10 \ - mvn install -B -V \ - -Dmaven.test.skip -DskipTests=true \ - -Dclirr.skip=true \ - -Denforcer.skip=true \ - -Dmaven.javadoc.skip=true \ - -Dgcloud.download.skip=true + run: .kokoro/build.sh shell: bash - - uses: GoogleCloudPlatform/cloud-opensource-java/linkage-monitor@v1-linkagemonitor + - name: Validate any conflicts with regard to com.google.cloud:libraries-bom (latest release) + uses: GoogleCloudPlatform/cloud-opensource-java/linkage-monitor@v1-linkagemonitor lint: runs-on: ubuntu-latest steps: diff --git a/synth.metadata b/synth.metadata index d7cc91b54ba..016c9d8aef1 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "7cdfb82b40487600547d0bad92119508161ca689" + "sha": "326b8331d952005cf48eaa9efb054fab5918f7e9" } }, { @@ -19,7 +19,7 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "2414b817065726eae0bc525346c7e874f969369d" + "sha": "f327d3b657a63ae4a8efd7f011a15eacae36b59c" } } ], From 44aa384429056dd6c6563351c43fe7dcac451008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Wed, 17 Feb 2021 04:20:11 +0100 Subject: [PATCH 39/42] feat!: add support for CommitStats (#544) * feat!: add support for CommitStats Adds support for returning CommitStats from read/write transactions. * fix: add clirr ignored differences * fix: error message should start with getCommitResponse Co-authored-by: skuruppu * fix: remove overload delay * chore: cleanup after merge * fix: update copyright years of new files * test: fix flaky test * test: skip commit stats tests on emulator * test: missed one commit stats tests against emulator * test: skip another emulator test * test: add missing test cases * fix: address review comments * chore: use junit assertion instead of truth * chore: replace truth asserts with junit asserts * chore: replace truth assertions with junit * chore: cleanup test and variable names * fix: rename test method and variables * fix: address review comments Co-authored-by: skuruppu --- .../clirr-ignored-differences.xml | 23 +++ .../com/google/cloud/spanner/AsyncRunner.java | 6 + .../google/cloud/spanner/AsyncRunnerImpl.java | 36 +++- .../spanner/AsyncTransactionManager.java | 3 + .../spanner/AsyncTransactionManagerImpl.java | 30 +++- .../google/cloud/spanner/CommitResponse.java | 30 +++- .../com/google/cloud/spanner/CommitStats.java | 54 ++++++ .../com/google/cloud/spanner/Options.java | 29 +++- .../com/google/cloud/spanner/SessionImpl.java | 20 +-- .../com/google/cloud/spanner/SessionPool.java | 41 ++++- .../SessionPoolAsyncTransactionManager.java | 18 ++ .../cloud/spanner/TransactionManager.java | 3 + .../cloud/spanner/TransactionManagerImpl.java | 10 +- .../cloud/spanner/TransactionRunner.java | 3 + .../cloud/spanner/TransactionRunnerImpl.java | 47 +++--- .../cloud/spanner/AsyncRunnerImplTest.java | 156 ++++++++++++++++++ .../google/cloud/spanner/AsyncRunnerTest.java | 24 +++ .../AsyncTransactionManagerImplTest.java | 53 ++++++ .../spanner/AsyncTransactionManagerTest.java | 28 ++++ .../cloud/spanner/BaseSessionPoolTest.java | 5 +- .../cloud/spanner/CommitResponseTest.java | 86 ++++++++++ .../cloud/spanner/DatabaseClientImplTest.java | 139 +++++++++++++--- .../spanner/InlineBeginTransactionTest.java | 8 +- .../cloud/spanner/MockSpannerServiceImpl.java | 12 +- .../com/google/cloud/spanner/OptionsTest.java | 55 +++++- .../RetryOnInvalidatedSessionTest.java | 13 +- .../google/cloud/spanner/SessionPoolTest.java | 22 ++- .../spanner/TransactionContextImplTest.java | 34 ++++ .../spanner/TransactionManagerImplTest.java | 20 +-- .../spanner/TransactionRunnerImplTest.java | 6 +- .../connection/ConnectionImplTest.java | 29 +++- .../connection/ReadWriteTransactionTest.java | 11 +- .../connection/SingleUseTransactionTest.java | 28 +++- .../cloud/spanner/it/ITAsyncAPITest.java | 89 +++++++++- .../spanner/it/ITTransactionManagerTest.java | 29 ++++ .../cloud/spanner/it/ITTransactionTest.java | 22 +++ .../google/cloud/spanner/it/ITWriteTest.java | 42 +++++ 37 files changed, 1111 insertions(+), 153 deletions(-) create mode 100644 google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitStats.java create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncRunnerImplTest.java create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/CommitResponseTest.java diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index 8dd5d9dad6d..fcafcb7c3a1 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -453,4 +453,27 @@ com/google/cloud/spanner/TransactionContext com.google.api.core.ApiFuture executeUpdateAsync(com.google.cloud.spanner.Statement) + + + + 7012 + com/google/cloud/spanner/AsyncTransactionManager + com.google.api.core.ApiFuture getCommitResponse() + + + 7012 + com/google/cloud/spanner/AsyncRunner + com.google.api.core.ApiFuture getCommitResponse() + + + 7012 + com/google/cloud/spanner/TransactionManager + com.google.cloud.spanner.CommitResponse getCommitResponse() + + + 7012 + com/google/cloud/spanner/TransactionRunner + com.google.cloud.spanner.CommitResponse getCommitResponse() + + diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunner.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunner.java index 3cae49e65b4..c9dec98d55d 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunner.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunner.java @@ -56,4 +56,10 @@ interface AsyncWork { * {@link ExecutionException} if the transaction did not commit. */ ApiFuture getCommitTimestamp(); + + /** + * Returns the {@link CommitResponse} of this transaction. {@link ApiFuture#get()} throws an + * {@link ExecutionException} if the transaction did not commit. + */ + ApiFuture getCommitResponse(); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunnerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunnerImpl.java index 5b83402919e..7982f0d2822 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunnerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunnerImpl.java @@ -16,23 +16,31 @@ package com.google.cloud.spanner; +import static com.google.common.base.Preconditions.checkState; + +import com.google.api.core.ApiFunction; import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; import com.google.api.core.SettableApiFuture; import com.google.cloud.Timestamp; import com.google.cloud.spanner.TransactionRunner.TransactionCallable; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.MoreExecutors; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; class AsyncRunnerImpl implements AsyncRunner { private final TransactionRunnerImpl delegate; - private final SettableApiFuture commitTimestamp = SettableApiFuture.create(); + private SettableApiFuture commitResponse; AsyncRunnerImpl(TransactionRunnerImpl delegate) { - this.delegate = delegate; + this.delegate = Preconditions.checkNotNull(delegate); } @Override public ApiFuture runAsync(final AsyncWork work, Executor executor) { + Preconditions.checkState(commitResponse == null, "runAsync() can only be called once"); + commitResponse = SettableApiFuture.create(); final SettableApiFuture res = SettableApiFuture.create(); executor.execute( new Runnable() { @@ -43,7 +51,7 @@ public void run() { } catch (Throwable t) { res.setException(t); } finally { - setCommitTimestamp(); + setCommitResponse(); } } }); @@ -66,16 +74,30 @@ public R run(TransactionContext transaction) throws Exception { }); } - private void setCommitTimestamp() { + private void setCommitResponse() { try { - commitTimestamp.set(delegate.getCommitTimestamp()); + commitResponse.set(delegate.getCommitResponse()); } catch (Throwable t) { - commitTimestamp.setException(t); + commitResponse.setException(t); } } @Override public ApiFuture getCommitTimestamp() { - return commitTimestamp; + checkState(commitResponse != null, "runAsync() has not yet been called"); + return ApiFutures.transform( + commitResponse, + new ApiFunction() { + @Override + public Timestamp apply(CommitResponse input) { + return input.getCommitTimestamp(); + } + }, + MoreExecutors.directExecutor()); + } + + public ApiFuture getCommitResponse() { + checkState(commitResponse != null, "runAsync() has not yet been called"); + return commitResponse; } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java index 02d4a9dbd23..a4741dd3293 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java @@ -191,6 +191,9 @@ public interface AsyncTransactionFunction { /** Returns the state of the transaction. */ TransactionState getState(); + /** Returns the {@link CommitResponse} of this transaction. */ + ApiFuture getCommitResponse(); + /** * Closes the manager. If there is an active transaction, it will be rolled back. Underlying * session will be released back to the session pool. 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 8dab813015e..2ec914c24aa 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 @@ -17,6 +17,7 @@ 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; @@ -45,7 +46,7 @@ final class AsyncTransactionManagerImpl private TransactionRunnerImpl.TransactionContextImpl txn; private TransactionState txnState; - private final SettableApiFuture commitTimestamp = SettableApiFuture.create(); + private final SettableApiFuture commitResponse = SettableApiFuture.create(); AsyncTransactionManagerImpl(SessionImpl session, Span span, TransactionOption... options) { this.session = session; @@ -132,29 +133,37 @@ public ApiFuture commitAsync() { SpannerExceptionFactory.newSpannerException( ErrorCode.ABORTED, "Transaction already aborted")); } - ApiFuture res = txn.commitAsync(); + ApiFuture commitResponseFuture = txn.commitAsync(); txnState = TransactionState.COMMITTED; ApiFutures.addCallback( - res, - new ApiFutureCallback() { + commitResponseFuture, + new ApiFutureCallback() { @Override public void onFailure(Throwable t) { if (t instanceof AbortedException) { txnState = TransactionState.ABORTED; } else { txnState = TransactionState.COMMIT_FAILED; - commitTimestamp.setException(t); + commitResponse.setException(t); } } @Override - public void onSuccess(Timestamp result) { - commitTimestamp.set(result); + public void onSuccess(CommitResponse result) { + commitResponse.set(result); + } + }, + MoreExecutors.directExecutor()); + return ApiFutures.transform( + commitResponseFuture, + new ApiFunction() { + @Override + public Timestamp apply(CommitResponse input) { + return input.getCommitTimestamp(); } }, MoreExecutors.directExecutor()); - return res; } @Override @@ -187,6 +196,11 @@ public TransactionState getState() { return txnState; } + @Override + public ApiFuture getCommitResponse() { + return commitResponse; + } + @Override public void invalidate() { if (txnState == TransactionState.STARTED || txnState == null) { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitResponse.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitResponse.java index dd5534d7c35..00505cf8d1f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitResponse.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitResponse.java @@ -17,20 +17,38 @@ package com.google.cloud.spanner; import com.google.cloud.Timestamp; +import com.google.common.base.Preconditions; import java.util.Objects; /** Represents a response from a commit operation. */ public class CommitResponse { - private final Timestamp commitTimestamp; + private final com.google.spanner.v1.CommitResponse proto; public CommitResponse(Timestamp commitTimestamp) { - this.commitTimestamp = commitTimestamp; + this.proto = + com.google.spanner.v1.CommitResponse.newBuilder() + .setCommitTimestamp(commitTimestamp.toProto()) + .build(); } - /** Returns a {@link Timestamp} representing the commit time of the write operation. */ + CommitResponse(com.google.spanner.v1.CommitResponse proto) { + this.proto = Preconditions.checkNotNull(proto); + } + + /** Returns a {@link Timestamp} representing the commit time of the transaction. */ public Timestamp getCommitTimestamp() { - return commitTimestamp; + return Timestamp.fromProto(proto.getCommitTimestamp()); + } + + /** + * Commit statistics are returned by a read/write transaction if specifically requested by passing + * in {@link Options#commitStats()} to the transaction. + */ + public CommitStats getCommitStats() { + Preconditions.checkState( + proto.hasCommitStats(), "The CommitResponse does not contain any commit statistics."); + return CommitStats.fromProto(proto.getCommitStats()); } @Override @@ -42,11 +60,11 @@ public boolean equals(Object o) { return false; } CommitResponse that = (CommitResponse) o; - return Objects.equals(commitTimestamp, that.commitTimestamp); + return Objects.equals(proto, that.proto); } @Override public int hashCode() { - return Objects.hash(commitTimestamp); + return Objects.hash(proto); } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitStats.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitStats.java new file mode 100644 index 00000000000..eaf1a78819f --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitStats.java @@ -0,0 +1,54 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner; + +import com.google.common.base.Preconditions; + +/** + * Commit statistics are returned by a read/write transaction if specifically requested by passing + * in {@link Options#commitStats()} to the transaction. + */ +public class CommitStats { + private final long mutationCount; + + private CommitStats(long mutationCount) { + this.mutationCount = mutationCount; + } + + static CommitStats fromProto(com.google.spanner.v1.CommitResponse.CommitStats proto) { + Preconditions.checkNotNull(proto); + return new CommitStats(proto.getMutationCount()); + } + + /** + * The number of mutations that were executed by the transaction. Insert and update operations + * count with the multiplicity of the number of columns they affect. For example, inserting a new + * record may count as five mutations, if values are inserted into five columns. Delete and delete + * range operations count as one mutation regardless of the number of columns affected. Deleting a + * row from a parent table that has the ON DELETE CASCADE annotation is also counted as one + * mutation regardless of the number of interleaved child rows present. The exception to this is + * if there are secondary indexes defined on rows being deleted, then the changes to the secondary + * indexes are counted individually. For example, if a table has 2 secondary indexes, deleting a + * range of rows in the table counts as 1 mutation for the table, plus 2 mutations for each row + * that is deleted because the rows in the secondary index might be scattered over the key-space, + * making it impossible for Cloud Spanner to call a single delete range operation on the secondary + * indexes. Secondary indexes include the foreign keys backing indexes. + */ + public long getMutationCount() { + return mutationCount; + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java index 26d3cd68322..38932bf8a80 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java @@ -46,6 +46,11 @@ public interface UpdateOption {} /** Marker interface to mark options applicable to list operations in admin API. */ public interface ListOption {} + /** Specifying this instructs the transaction to request {@link CommitStats} from the backend. */ + public static TransactionOption commitStats() { + return COMMIT_STATS_OPTION; + } + /** * Specifying this will cause the read to yield at most this many rows. This should be greater * than 0. @@ -116,6 +121,16 @@ public static ListOption filter(String filter) { return new FilterOption(filter); } + /** Option to request {@link CommitStats} for read/write transactions. */ + static final class CommitStatsOption extends InternalOption implements TransactionOption { + @Override + void appendToOptions(Options options) { + options.withCommitStats = true; + } + } + + static final CommitStatsOption COMMIT_STATS_OPTION = new CommitStatsOption(); + /** Option pertaining to flow control. */ static final class FlowControlOption extends InternalOption implements ReadAndQueryOption { final int prefetchChunks; @@ -143,6 +158,7 @@ void appendToOptions(Options options) { } } + private boolean withCommitStats; private Long limit; private Integer prefetchChunks; private Integer bufferRows; @@ -153,6 +169,10 @@ void appendToOptions(Options options) { // Construction is via factory methods below. private Options() {} + boolean withCommitStats() { + return withCommitStats; + } + boolean hasLimit() { return limit != null; } @@ -204,6 +224,9 @@ String filter() { @Override public String toString() { StringBuilder b = new StringBuilder(); + if (withCommitStats) { + b.append("withCommitStats: ").append(withCommitStats).append(' '); + } if (limit != null) { b.append("limit: ").append(limit).append(' '); } @@ -234,7 +257,8 @@ public boolean equals(Object o) { } Options that = (Options) o; - return (!hasLimit() && !that.hasLimit() + return Objects.equals(withCommitStats, that.withCommitStats) + && (!hasLimit() && !that.hasLimit() || hasLimit() && that.hasLimit() && Objects.equals(limit(), that.limit())) && (!hasPrefetchChunks() && !that.hasPrefetchChunks() || hasPrefetchChunks() @@ -253,6 +277,9 @@ public boolean equals(Object o) { @Override public int hashCode() { int result = 31; + if (withCommitStats) { + result = 31 * result + 1231; + } if (limit != null) { result = 31 * result + limit.hashCode(); } 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 a20a6b7b248..51ec7b4d50d 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 @@ -147,7 +147,7 @@ public Void run(TransactionContext ctx) { return null; } }); - return new CommitResponse(runner.getCommitTimestamp()); + return runner.getCommitResponse(); } @Override @@ -165,6 +165,8 @@ public CommitResponse writeAtLeastOnceWithOptions( final CommitRequest request = CommitRequest.newBuilder() .setSession(name) + .setReturnCommitStats( + Options.fromTransactionOptions(transactionOptions).withCommitStats()) .addAllMutations(mutationsProto) .setSingleUseTransaction( TransactionOptions.newBuilder() @@ -174,11 +176,7 @@ public CommitResponse writeAtLeastOnceWithOptions( try (Scope s = tracer.withSpan(span)) { com.google.spanner.v1.CommitResponse response = spanner.getRpc().commit(request, this.options); - Timestamp t = Timestamp.fromProto(response.getCommitTimestamp()); - return new CommitResponse(t); - } catch (IllegalArgumentException e) { - TraceUtil.setWithFailure(span, e); - throw newSpannerException(ErrorCode.INTERNAL, "Could not parse commit timestamp", e); + return new CommitResponse(response); } catch (RuntimeException e) { TraceUtil.setWithFailure(span, e); throw e; @@ -246,17 +244,12 @@ public ReadOnlyTransaction readOnlyTransaction(TimestampBound bound) { @Override public TransactionRunner readWriteTransaction(TransactionOption... options) { - return setActive( - new TransactionRunnerImpl( - this, spanner.getRpc(), spanner.getDefaultPrefetchChunks(), options)); + return setActive(new TransactionRunnerImpl(this, options)); } @Override public AsyncRunner runAsync(TransactionOption... options) { - return new AsyncRunnerImpl( - setActive( - new TransactionRunnerImpl( - this, spanner.getRpc(), spanner.getDefaultPrefetchChunks(), options))); + return new AsyncRunnerImpl(setActive(new TransactionRunnerImpl(this, options))); } @Override @@ -350,6 +343,7 @@ public void run() { TransactionContextImpl newTransaction(Options options) { return TransactionContextImpl.newBuilder() .setSession(this) + .setOptions(options) .setTransactionId(readyTransactionId) .setOptions(options) .setTrackTransactionStarter(spanner.getOptions().isTrackTransactionStarter()) 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 b24393340a4..51a42525bfc 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 @@ -37,6 +37,7 @@ import static com.google.cloud.spanner.MetricRegistryConstants.SPANNER_LABEL_KEYS; import static com.google.cloud.spanner.MetricRegistryConstants.SPANNER_LABEL_KEYS_WITH_TYPE; import static com.google.cloud.spanner.SpannerExceptionFactory.newSpannerException; +import static com.google.common.base.Preconditions.checkState; import com.google.api.core.ApiFunction; import com.google.api.core.ApiFuture; @@ -860,7 +861,7 @@ public TransactionContext resetForRetry() { } catch (SessionNotFoundException e) { session = sessionPool.replaceSession(e, session); PooledSession pooledSession = session.get(); - delegate = pooledSession.delegate.transactionManager(); + delegate = pooledSession.delegate.transactionManager(options); restartedAfterSessionNotFound = true; } } @@ -871,6 +872,11 @@ public Timestamp getCommitTimestamp() { return delegate.getCommitTimestamp(); } + @Override + public CommitResponse getCommitResponse() { + return delegate.getCommitResponse(); + } + @Override public void close() { if (closed) { @@ -949,6 +955,11 @@ public Timestamp getCommitTimestamp() { return getRunner().getCommitTimestamp(); } + @Override + public CommitResponse getCommitResponse() { + return getRunner().getCommitResponse(); + } + @Override public TransactionRunner allowNestedTransaction() { getRunner().allowNestedTransaction(); @@ -960,7 +971,7 @@ private static class SessionPoolAsyncRunner implements AsyncRunner { private final SessionPool sessionPool; private volatile PooledSessionFuture session; private final TransactionOption[] options; - private final SettableApiFuture commitTimestamp = SettableApiFuture.create(); + private SettableApiFuture commitResponse; private SessionPoolAsyncRunner( SessionPool sessionPool, PooledSessionFuture session, TransactionOption... options) { @@ -971,6 +982,7 @@ private SessionPoolAsyncRunner( @Override public ApiFuture runAsync(final AsyncWork work, Executor executor) { + commitResponse = SettableApiFuture.create(); final SettableApiFuture res = SettableApiFuture.create(); executor.execute( new Runnable() { @@ -1010,7 +1022,7 @@ public void run() { } session.get().markUsed(); session.close(); - setCommitTimestamp(runner); + setCommitResponse(runner); if (exception != null) { res.setException(exception); } else { @@ -1021,17 +1033,32 @@ public void run() { return res; } - private void setCommitTimestamp(AsyncRunner delegate) { + private void setCommitResponse(AsyncRunner delegate) { try { - commitTimestamp.set(delegate.getCommitTimestamp().get()); + commitResponse.set(delegate.getCommitResponse().get()); } catch (Throwable t) { - commitTimestamp.setException(t); + commitResponse.setException(t); } } @Override public ApiFuture getCommitTimestamp() { - return commitTimestamp; + checkState(commitResponse != null, "runAsync() has not yet been called"); + return ApiFutures.transform( + commitResponse, + new ApiFunction() { + @Override + public Timestamp apply(CommitResponse input) { + return input.getCommitTimestamp(); + } + }, + MoreExecutors.directExecutor()); + } + + @Override + public ApiFuture getCommitResponse() { + checkState(commitResponse != null, "runAsync() has not yet been called"); + return commitResponse; } } 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 e618e2b44a3..83056603f28 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 @@ -290,4 +290,22 @@ public TransactionState getState() { return txnState; } } + + public ApiFuture getCommitResponse() { + synchronized (lock) { + Preconditions.checkState( + txnState == TransactionState.COMMITTED, + "commit can only be invoked if the transaction was successfully committed"); + } + return ApiFutures.transformAsync( + delegate, + new ApiAsyncFunction() { + @Override + public ApiFuture apply(AsyncTransactionManagerImpl input) + throws Exception { + return input.getCommitResponse(); + } + }, + MoreExecutors.directExecutor()); + } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManager.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManager.java index 34e21657152..c9a56d8100a 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManager.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManager.java @@ -87,6 +87,9 @@ public enum TransactionState { */ Timestamp getCommitTimestamp(); + /** Returns the {@link CommitResponse} of this transaction. */ + CommitResponse getCommitResponse(); + /** Returns the state of the transaction. */ TransactionState getState(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java index 1cef304e483..2d47fcd5c78 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java @@ -118,7 +118,15 @@ public Timestamp getCommitTimestamp() { Preconditions.checkState( txnState == TransactionState.COMMITTED, "getCommitTimestamp can only be invoked if the transaction committed successfully"); - return txn.commitTimestamp(); + return txn.getCommitResponse().getCommitTimestamp(); + } + + @Override + public CommitResponse getCommitResponse() { + Preconditions.checkState( + txnState == TransactionState.COMMITTED, + "getCommitResponse can only be invoked if the transaction committed successfully"); + return txn.getCommitResponse(); } @Override diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunner.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunner.java index 2ad27fcd7b5..33cb57e90fe 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunner.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunner.java @@ -74,6 +74,9 @@ interface TransactionCallable { */ Timestamp getCommitTimestamp(); + /** Returns the {@link CommitResponse} of this transaction. */ + CommitResponse getCommitResponse(); + /** * Allows overriding the default behaviour of blocking nested transactions. * diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java index a4973c29345..dbbbca068a0 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java @@ -31,7 +31,6 @@ import com.google.cloud.spanner.Options.TransactionOption; import com.google.cloud.spanner.Options.UpdateOption; import com.google.cloud.spanner.SessionImpl.SessionTransaction; -import com.google.cloud.spanner.spi.v1.SpannerRpc; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; @@ -40,7 +39,6 @@ import com.google.protobuf.Empty; import com.google.rpc.Code; import com.google.spanner.v1.CommitRequest; -import com.google.spanner.v1.CommitResponse; import com.google.spanner.v1.ExecuteBatchDmlRequest; import com.google.spanner.v1.ExecuteBatchDmlResponse; import com.google.spanner.v1.ExecuteSqlRequest; @@ -184,7 +182,7 @@ public void removeListener(Runnable listener) { volatile ByteString transactionId; - private Timestamp commitTimestamp; + private CommitResponse commitResponse; private TransactionContextImpl(Builder builder) { super(builder); @@ -275,7 +273,7 @@ public void run() { void commit() { try { - commitTimestamp = commitAsync().get(); + commitResponse = commitAsync().get(); } catch (InterruptedException e) { if (commitFuture != null) { commitFuture.cancel(true); @@ -288,10 +286,13 @@ void commit() { volatile ApiFuture commitFuture; - ApiFuture commitAsync() { - final SettableApiFuture res = SettableApiFuture.create(); + ApiFuture commitAsync() { + final SettableApiFuture res = SettableApiFuture.create(); final SettableApiFuture finishOps; - CommitRequest.Builder builder = CommitRequest.newBuilder().setSession(session.getName()); + CommitRequest.Builder builder = + CommitRequest.newBuilder() + .setSession(session.getName()) + .setReturnCommitStats(options.withCommitStats()); synchronized (lock) { if (transactionIdFuture == null && transactionId == null && runningAsyncOperations == 0) { finishOps = SettableApiFuture.create(); @@ -313,12 +314,12 @@ ApiFuture commitAsync() { } private final class CommitRunnable implements Runnable { - private final SettableApiFuture res; + private final SettableApiFuture res; private final ApiFuture prev; private final CommitRequest.Builder requestBuilder; CommitRunnable( - SettableApiFuture res, + SettableApiFuture res, ApiFuture prev, CommitRequest.Builder requestBuilder) { this.res = res; @@ -342,7 +343,7 @@ public void run() { span.addAnnotation("Starting Commit"); final Span opSpan = tracer.spanBuilderWithExplicitParent(SpannerImpl.COMMIT, span).startSpan(); - final ApiFuture commitFuture = + final ApiFuture commitFuture = rpc.commitAsync(commitRequest, session.getOptions()); commitFuture.addListener( tracer.withSpan( @@ -351,15 +352,14 @@ public void run() { @Override public void run() { try { - CommitResponse commitResponse = commitFuture.get(); - if (!commitResponse.hasCommitTimestamp()) { + com.google.spanner.v1.CommitResponse proto = commitFuture.get(); + if (!proto.hasCommitTimestamp()) { throw newSpannerException( ErrorCode.INTERNAL, "Missing commitTimestamp:\n" + session.getName()); } - Timestamp ts = Timestamp.fromProto(commitResponse.getCommitTimestamp()); span.addAnnotation("Commit Done"); opSpan.end(TraceUtil.END_SPAN_OPTIONS); - res.set(ts); + res.set(new CommitResponse(proto)); } catch (Throwable e) { if (e instanceof ExecutionException) { e = @@ -387,9 +387,9 @@ public void run() { } } - Timestamp commitTimestamp() { - checkState(commitTimestamp != null, "run() has not yet returned normally"); - return commitTimestamp; + CommitResponse getCommitResponse() { + checkState(commitResponse != null, "run() has not yet returned normally"); + return commitResponse; } boolean isAborted() { @@ -826,11 +826,7 @@ public TransactionRunner allowNestedTransaction() { return this; } - TransactionRunnerImpl( - SessionImpl session, - SpannerRpc rpc, - int defaultPrefetchChunks, - TransactionOption... options) { + TransactionRunnerImpl(SessionImpl session, TransactionOption... options) { this.session = session; this.options = Options.fromTransactionOptions(options); this.txn = session.newTransaction(this.options); @@ -955,7 +951,12 @@ public T call() { @Override public Timestamp getCommitTimestamp() { checkState(txn != null, "run() has not yet returned normally"); - return txn.commitTimestamp(); + return txn.getCommitResponse().getCommitTimestamp(); + } + + public CommitResponse getCommitResponse() { + checkState(txn != null, "run() has not yet returned normally"); + return txn.getCommitResponse(); } @Override diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncRunnerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncRunnerImplTest.java new file mode 100644 index 00000000000..fab29d29005 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncRunnerImplTest.java @@ -0,0 +1,156 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner; + +import static com.google.cloud.spanner.SpannerApiFutures.get; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; +import com.google.cloud.spanner.AsyncRunner.AsyncWork; +import com.google.cloud.spanner.TransactionRunner.TransactionCallable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.junit.AfterClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class AsyncRunnerImplTest { + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); + + @AfterClass + public static void teardown() { + executor.shutdown(); + } + + @SuppressWarnings("unchecked") + @Test + public void testAsyncRunReturnsResultAndCommitResponse() { + final Object expectedResult = new Object(); + final CommitResponse expectedCommitResponse = mock(CommitResponse.class); + + TransactionRunnerImpl delegate = mock(TransactionRunnerImpl.class); + when(delegate.run(any(TransactionCallable.class))).thenReturn(expectedResult); + when(delegate.getCommitResponse()).thenReturn(expectedCommitResponse); + + AsyncRunnerImpl runner = new AsyncRunnerImpl(delegate); + ApiFuture result = + runner.runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + return ApiFutures.immediateFuture(expectedResult); + } + }, + executor); + + assertSame(expectedResult, get(result)); + assertSame(expectedCommitResponse, get(runner.getCommitResponse())); + assertEquals( + get(runner.getCommitResponse()).getCommitTimestamp(), get(runner.getCommitTimestamp())); + } + + @Test + public void testGetCommitTimestampReturnsErrorBeforeRun() { + TransactionRunnerImpl delegate = mock(TransactionRunnerImpl.class); + AsyncRunnerImpl runner = new AsyncRunnerImpl(delegate); + try { + runner.getCommitTimestamp(); + fail("missing expected exception"); + } catch (IllegalStateException e) { + assertTrue(e.getMessage().contains("runAsync() has not yet been called")); + } + } + + @Test + public void testGetCommitResponseReturnsErrorBeforeRun() { + TransactionRunnerImpl delegate = mock(TransactionRunnerImpl.class); + AsyncRunnerImpl runner = new AsyncRunnerImpl(delegate); + try { + runner.getCommitResponse(); + fail("missing expected exception"); + } catch (IllegalStateException e) { + assertTrue(e.getMessage().contains("runAsync() has not yet been called")); + } + } + + @Test + public void testGetCommitResponseReturnsErrorIfRunFails() { + final SpannerException expectedException = + SpannerExceptionFactory.newSpannerException(ErrorCode.ALREADY_EXISTS, "Row already exists"); + + TransactionRunnerImpl delegate = mock(TransactionRunnerImpl.class); + when(delegate.getCommitResponse()).thenThrow(expectedException); + + AsyncRunnerImpl runner = new AsyncRunnerImpl(delegate); + runner.runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + return ApiFutures.immediateFailedFuture(expectedException); + } + }, + executor); + + try { + get(runner.getCommitResponse()); + fail("missing expected exception"); + } catch (SpannerException e) { + assertSame(expectedException, e); + } + } + + @SuppressWarnings("unchecked") + @Test + public void testRunAyncFailsIfCalledMultipleTimes() { + final Object result = new Object(); + TransactionRunnerImpl delegate = mock(TransactionRunnerImpl.class); + when(delegate.run(any(TransactionCallable.class))).thenReturn(result); + + AsyncRunnerImpl runner = new AsyncRunnerImpl(delegate); + runner.runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + return ApiFutures.immediateFuture(result); + } + }, + executor); + + try { + runner.runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + return ApiFutures.immediateFuture(null); + } + }, + executor); + fail("missing expected exception"); + } catch (IllegalStateException e) { + assertTrue(e.getMessage().contains("runAsync() can only be called once")); + } + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncRunnerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncRunnerTest.java index 2af185ae144..0c67871e899 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncRunnerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncRunnerTest.java @@ -18,6 +18,7 @@ import static com.google.cloud.spanner.MockSpannerTestUtil.*; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.api.core.ApiFunction; @@ -53,6 +54,29 @@ @RunWith(JUnit4.class) public class AsyncRunnerTest extends AbstractAsyncTransactionTest { + + @Test + public void testAsyncRunner_doesNotReturnCommitTimestampBeforeCommit() { + AsyncRunner runner = client().runAsync(); + try { + runner.getCommitTimestamp(); + fail("missing expected exception"); + } catch (IllegalStateException e) { + assertTrue(e.getMessage().contains("runAsync() has not yet been called")); + } + } + + @Test + public void testAsyncRunner_doesNotReturnCommitResponseBeforeCommit() { + AsyncRunner runner = client().runAsync(); + try { + runner.getCommitResponse(); + fail("missing expected exception"); + } catch (IllegalStateException e) { + assertTrue(e.getMessage().contains("runAsync() has not yet been called")); + } + } + @Test public void asyncRunnerUpdate() throws Exception { AsyncRunner runner = client().runAsync(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java new file mode 100644 index 00000000000..c60363e7932 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.api.core.ApiFutures; +import com.google.cloud.Timestamp; +import io.opencensus.trace.Span; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class AsyncTransactionManagerImplTest { + + @Mock private SessionImpl session; + @Mock TransactionRunnerImpl.TransactionContextImpl transaction; + + @Test + public void testCommitReturnsCommitStats() { + try (AsyncTransactionManagerImpl manager = + new AsyncTransactionManagerImpl(session, mock(Span.class), Options.commitStats())) { + when(session.newTransaction(Options.fromTransactionOptions(Options.commitStats()))) + .thenReturn(transaction); + when(transaction.ensureTxnAsync()).thenReturn(ApiFutures.immediateFuture(null)); + Timestamp commitTimestamp = Timestamp.ofTimeMicroseconds(1); + CommitResponse response = mock(CommitResponse.class); + when(response.getCommitTimestamp()).thenReturn(commitTimestamp); + when(transaction.commitAsync()).thenReturn(ApiFutures.immediateFuture(response)); + manager.beginAsync(); + manager.commitAsync(); + verify(transaction).commitAsync(); + } + } +} 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 aed844ed9ad..748d6f76405 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 @@ -23,6 +23,8 @@ import static com.google.cloud.spanner.MockSpannerTestUtil.UPDATE_COUNT; import static com.google.cloud.spanner.MockSpannerTestUtil.UPDATE_STATEMENT; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import com.google.api.core.ApiFuture; @@ -213,6 +215,32 @@ public boolean apply(AbstractMessage input) { 0L); } + @Test + public void testAsyncTransactionManager_returnsCommitStats() throws Exception { + try (AsyncTransactionManager manager = + client().transactionManagerAsync(Options.commitStats())) { + TransactionContextFuture transaction = manager.beginAsync(); + while (true) { + try { + CommitTimestampFuture commitTimestamp = + transaction + .then( + AsyncTransactionManagerHelper.buffer( + Mutation.delete("FOO", Key.of("foo"))), + executor) + .commitAsync(); + assertNotNull(commitTimestamp.get()); + assertNotNull(manager.getCommitResponse().get()); + assertNotNull(manager.getCommitResponse().get().getCommitStats()); + assertEquals(1L, manager.getCommitResponse().get().getCommitStats().getMutationCount()); + break; + } catch (AbortedException e) { + transaction = manager.resetForRetryAsync(); + } + } + } + } + @Test public void asyncTransactionManagerUpdate() throws Exception { final SettableApiFuture updateCount = SettableApiFuture.create(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BaseSessionPoolTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BaseSessionPoolTest.java index 6fd6a4383e3..3db2bdb1c4f 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BaseSessionPoolTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BaseSessionPoolTest.java @@ -24,7 +24,6 @@ import static org.mockito.Mockito.when; import com.google.api.core.ApiFutures; -import com.google.cloud.Timestamp; import com.google.cloud.grpc.GrpcTransportOptions.ExecutorFactory; import com.google.cloud.spanner.SessionPool.Clock; import com.google.protobuf.Empty; @@ -67,9 +66,9 @@ SessionImpl mockSession() { "projects/dummy/instances/dummy/database/dummy/sessions/session" + sessionIndex); when(session.asyncClose()).thenReturn(ApiFutures.immediateFuture(Empty.getDefaultInstance())); when(session.writeWithOptions(any(Iterable.class))) - .thenReturn(new CommitResponse(Timestamp.now())); + .thenReturn(new CommitResponse(com.google.spanner.v1.CommitResponse.getDefaultInstance())); when(session.writeAtLeastOnceWithOptions(any(Iterable.class))) - .thenReturn(new CommitResponse(Timestamp.now())); + .thenReturn(new CommitResponse(com.google.spanner.v1.CommitResponse.getDefaultInstance())); sessionIndex++; return session; } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/CommitResponseTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/CommitResponseTest.java new file mode 100644 index 00000000000..7fb4c559d4d --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/CommitResponseTest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import com.google.cloud.Timestamp; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class CommitResponseTest { + + @Test + public void testConstructWithTimestamp() { + Timestamp timestamp = Timestamp.ofTimeSecondsAndNanos(100L, 100); + CommitResponse response = new CommitResponse(timestamp); + assertEquals(timestamp, response.getCommitTimestamp()); + } + + @Test + public void testFromProto() { + long mutationCount = 5L; + com.google.protobuf.Timestamp timestamp = + com.google.protobuf.Timestamp.newBuilder().setSeconds(123L).setNanos(456).build(); + com.google.spanner.v1.CommitResponse proto = + com.google.spanner.v1.CommitResponse.newBuilder() + .setCommitStats( + com.google.spanner.v1.CommitResponse.CommitStats.newBuilder() + .setMutationCount(mutationCount) + .build()) + .setCommitTimestamp(timestamp) + .build(); + + CommitResponse response = new CommitResponse(proto); + + assertEquals(Timestamp.ofTimeSecondsAndNanos(123L, 456), response.getCommitTimestamp()); + assertEquals(mutationCount, response.getCommitStats().getMutationCount()); + } + + @Test + public void testEqualsAndHashCode() { + com.google.spanner.v1.CommitResponse proto1 = + com.google.spanner.v1.CommitResponse.newBuilder() + .setCommitTimestamp(com.google.protobuf.Timestamp.newBuilder().setSeconds(1L).build()) + .build(); + com.google.spanner.v1.CommitResponse proto2 = + com.google.spanner.v1.CommitResponse.newBuilder() + .setCommitTimestamp(com.google.protobuf.Timestamp.newBuilder().setSeconds(2L).build()) + .build(); + com.google.spanner.v1.CommitResponse proto3 = + com.google.spanner.v1.CommitResponse.newBuilder() + .setCommitTimestamp(com.google.protobuf.Timestamp.newBuilder().setSeconds(1L).build()) + .build(); + + CommitResponse response1 = new CommitResponse(proto1); + CommitResponse response2 = new CommitResponse(proto2); + CommitResponse response3 = new CommitResponse(proto3); + + assertEquals(response3, response1); + assertNotEquals(response2, response1); + assertNotEquals(response3, response2); + assertNotEquals(response1, null); + assertNotEquals(response1, new Object()); + + assertEquals(response3.hashCode(), response1.hashCode()); + assertNotEquals(response2.hashCode(), response1.hashCode()); + assertNotEquals(response3.hashCode(), response2.hashCode()); + } +} 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 8ed55e0b9e2..42a158d755b 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 @@ -17,7 +17,11 @@ package com.google.cloud.spanner; import static com.google.cloud.spanner.MockSpannerTestUtil.SELECT1; +import static com.google.cloud.spanner.SpannerApiFutures.get; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -28,6 +32,7 @@ import com.google.api.gax.grpc.testing.LocalChannelProvider; import com.google.api.gax.retrying.RetrySettings; import com.google.cloud.NoCredentials; +import com.google.cloud.Timestamp; import com.google.cloud.spanner.AbstractResultSet.GrpcStreamIterator; import com.google.cloud.spanner.AsyncResultSet.CallbackResponse; import com.google.cloud.spanner.AsyncResultSet.ReadyCallback; @@ -155,21 +160,53 @@ public void tearDown() { } @Test - public void write() { + public void testWrite() { DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); - client.write( - Arrays.asList( - Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build())); + Timestamp timestamp = + client.write( + Arrays.asList( + Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build())); + assertNotNull(timestamp); } @Test - public void writeAtLeastOnce() { + public void testWriteWithCommitStats() { DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); - client.writeAtLeastOnce( - Arrays.asList( - Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build())); + CommitResponse response = + client.writeWithOptions( + Arrays.asList( + Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build()), + Options.commitStats()); + assertNotNull(response); + assertNotNull(response.getCommitTimestamp()); + assertNotNull(response.getCommitStats()); + } + + @Test + public void testWriteAtLeastOnce() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + Timestamp timestamp = + client.writeAtLeastOnce( + Arrays.asList( + Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build())); + assertNotNull(timestamp); + } + + @Test + public void testWriteAtLeastOnceWithCommitStats() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + CommitResponse response = + client.writeAtLeastOnceWithOptions( + Arrays.asList( + Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build()), + Options.commitStats()); + assertNotNull(response); + assertNotNull(response.getCommitTimestamp()); + assertNotNull(response.getCommitStats()); } @Test @@ -426,7 +463,7 @@ public void readOnlyTransactionBoundIsNonBlocking() { } @Test - public void readWriteTransaction() { + public void testReadWriteTransaction() { DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); TransactionRunner runner = client.readWriteTransaction(); @@ -438,6 +475,25 @@ public Void run(TransactionContext transaction) throws Exception { return null; } }); + assertNotNull(runner.getCommitTimestamp()); + } + + @Test + public void testReadWriteTransaction_returnsCommitStats() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + TransactionRunner runner = client.readWriteTransaction(Options.commitStats()); + runner.run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + transaction.buffer(Mutation.delete("FOO", Key.of("foo"))); + return null; + } + }); + assertNotNull(runner.getCommitResponse()); + assertNotNull(runner.getCommitResponse().getCommitStats()); + assertEquals(1L, runner.getCommitResponse().getCommitStats().getMutationCount()); } @Test @@ -461,12 +517,12 @@ public Void run(TransactionContext transaction) throws Exception { } @Test - public void runAsync() throws Exception { + public void testRunAsync() throws Exception { DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); ExecutorService executor = Executors.newSingleThreadExecutor(); AsyncRunner runner = client.runAsync(); - ApiFuture fut = + ApiFuture result = runner.runAsync( new AsyncWork() { @Override @@ -475,7 +531,31 @@ public ApiFuture doWorkAsync(TransactionContext txn) { } }, executor); - assertThat(fut.get()).isEqualTo(UPDATE_COUNT); + assertEquals(UPDATE_COUNT, result.get().longValue()); + assertNotNull(runner.getCommitTimestamp().get()); + executor.shutdown(); + } + + @Test + public void testRunAsync_returnsCommitStats() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + ExecutorService executor = Executors.newSingleThreadExecutor(); + AsyncRunner runner = client.runAsync(Options.commitStats()); + ApiFuture result = + runner.runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + txn.buffer(Mutation.delete("FOO", Key.of("foo"))); + return ApiFutures.immediateFuture(null); + } + }, + executor); + assertNull(get(result)); + assertNotNull(get(runner.getCommitResponse())); + assertNotNull(get(runner.getCommitResponse()).getCommitStats()); + assertEquals(1L, get(runner.getCommitResponse()).getCommitStats().getMutationCount()); executor.shutdown(); } @@ -528,19 +608,40 @@ public ApiFuture doWorkAsync(TransactionContext txn) { } @Test - public void transactionManager() throws Exception { + public void testTransactionManager() throws Exception { DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); - try (TransactionManager txManager = client.transactionManager()) { + try (TransactionManager manager = client.transactionManager()) { while (true) { - TransactionContext tx = txManager.begin(); + TransactionContext transaction = manager.begin(); try { - tx.executeUpdate(UPDATE_STATEMENT); - txManager.commit(); + transaction.executeUpdate(UPDATE_STATEMENT); + manager.commit(); + assertNotNull(manager.getCommitTimestamp()); break; } catch (AbortedException e) { - Thread.sleep(e.getRetryDelayInMillis() / 1000); - tx = txManager.resetForRetry(); + transaction = manager.resetForRetry(); + } + } + } + } + + @Test + public void testTransactionManager_returnsCommitStats() throws InterruptedException { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + try (TransactionManager manager = client.transactionManager(Options.commitStats())) { + while (true) { + TransactionContext transaction = manager.begin(); + try { + transaction.buffer(Mutation.delete("FOO", Key.of("foo"))); + manager.commit(); + assertNotNull(manager.getCommitResponse()); + assertNotNull(manager.getCommitResponse().getCommitStats()); + assertEquals(1L, manager.getCommitResponse().getCommitStats().getMutationCount()); + break; + } catch (AbortedException e) { + transaction = manager.resetForRetry(); } } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java index aeeac5b3d11..5aa2948b518 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java @@ -1473,13 +1473,12 @@ public Void run(TransactionContext transaction) throws Exception { if (attempt == 1) { impl.waitForTransactionTimeoutMillis = 1L; // Freeze the mock server to prevent the first (async) statement from returning - // a - // transaction. + // a transaction. mockSpanner.freeze(); } else { impl.waitForTransactionTimeoutMillis = 60_000L; } - transaction.executeUpdateAsync(UPDATE_STATEMENT); + ApiFuture updateCount = transaction.executeUpdateAsync(UPDATE_STATEMENT); // Try to execute a query. This will timeout during the first attempt while // waiting @@ -1489,6 +1488,9 @@ public Void run(TransactionContext transaction) throws Exception { while (rs.next()) {} } catch (Throwable t) { mockSpanner.unfreeze(); + // Wait until the update actually finishes so it has returned a transaction. + // This ensures that the retry does not issue a BeginTransaction RPC. + SpannerApiFutures.get(updateCount); throw t; } return null; 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 17714ebae0c..424e9796923 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 @@ -1809,8 +1809,16 @@ public void commit(CommitRequest request, StreamObserver respons } simulateAbort(session, request.getTransactionId()); commitTransaction(transaction.getId()); - responseObserver.onNext( - CommitResponse.newBuilder().setCommitTimestamp(getCurrentGoogleTimestamp()).build()); + CommitResponse.Builder responseBuilder = + CommitResponse.newBuilder().setCommitTimestamp(getCurrentGoogleTimestamp()); + if (request.getReturnCommitStats()) { + responseBuilder.setCommitStats( + com.google.spanner.v1.CommitResponse.CommitStats.newBuilder() + // This is not really always equal, but at least it returns a value. + .setMutationCount(request.getMutationsCount()) + .build()); + } + responseObserver.onNext(responseBuilder.build()); responseObserver.onCompleted(); } catch (StatusRuntimeException t) { responseObserver.onError(t); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OptionsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OptionsTest.java index 5a2777570b7..db642aedd12 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OptionsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OptionsTest.java @@ -17,6 +17,8 @@ package com.google.cloud.spanner; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; @@ -221,22 +223,57 @@ public void queryEquality() { } @Test - public void testFromTransactionOptions() { + public void testFromTransactionOptions_toStringNoOptions() { Options opts = Options.fromTransactionOptions(); assertThat(opts.toString()).isEqualTo(""); } @Test - public void testTransactionOptionsEquality() { - Options o1; - Options o2; + public void testFromTransactionOptions_toStringWithCommitStats() { + Options options = Options.fromTransactionOptions(Options.commitStats()); + assertThat(options.toString()).contains("withCommitStats: true"); + } - o1 = Options.fromTransactionOptions(); - o2 = Options.fromTransactionOptions(); - assertThat(o1.equals(o2)).isTrue(); + @Test + public void testTransactionOptions_noOptionsAreEqual() { + Options option1 = Options.fromTransactionOptions(); + Options option2 = Options.fromTransactionOptions(); + assertEquals(option1, option2); + } - o2 = Options.fromReadOptions(Options.prefetchChunks(1)); - assertThat(o1.equals(o2)).isFalse(); + @Test + public void testTransactionOptions_withCommitStatsAreEqual() { + Options option1 = Options.fromTransactionOptions(Options.commitStats()); + Options option2 = Options.fromTransactionOptions(Options.commitStats()); + assertEquals(option1, option2); + } + + @Test + public void testTransactionOptions_withCommitStatsAndOtherOptionAreNotEqual() { + Options option1 = Options.fromTransactionOptions(Options.commitStats()); + Options option2 = Options.fromQueryOptions(Options.prefetchChunks(10)); + assertNotEquals(option1, option2); + } + + @Test + public void testTransactionOptions_noOptionsHashCode() { + Options option1 = Options.fromTransactionOptions(); + Options option2 = Options.fromTransactionOptions(); + assertEquals(option2.hashCode(), option1.hashCode()); + } + + @Test + public void testTransactionOptions_withCommitStatsHashCode() { + Options option1 = Options.fromTransactionOptions(Options.commitStats()); + Options option2 = Options.fromTransactionOptions(Options.commitStats()); + assertEquals(option2.hashCode(), option1.hashCode()); + } + + @Test + public void testTransactionOptions_withCommitStatsAndOtherOptionHashCode() { + Options option1 = Options.fromTransactionOptions(Options.commitStats()); + Options option2 = Options.fromQueryOptions(Options.prefetchChunks(10)); + assertNotEquals(option2.hashCode(), option1.hashCode()); } @Test 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 e49c7e739cc..157d71bf367 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 @@ -18,6 +18,10 @@ import static com.google.cloud.spanner.SpannerApiFutures.get; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.api.core.ApiFunction; @@ -1151,7 +1155,7 @@ public void transactionManagerReadRowUsingIndex() throws InterruptedException { @Test public void transactionManagerUpdate() throws InterruptedException { invalidateSessionPool(); - try (TransactionManager manager = client.transactionManager()) { + try (TransactionManager manager = client.transactionManager(Options.commitStats())) { long count; TransactionContext transaction = manager.begin(); while (true) { @@ -1164,10 +1168,11 @@ public void transactionManagerUpdate() throws InterruptedException { transaction = manager.resetForRetry(); } } - assertThat(count).isEqualTo(UPDATE_COUNT); - assertThat(failOnInvalidatedSession).isFalse(); + assertEquals(UPDATE_COUNT, count); + assertNotNull(manager.getCommitResponse().getCommitStats()); + assertFalse(failOnInvalidatedSession); } catch (SessionNotFoundException e) { - assertThat(failOnInvalidatedSession).isTrue(); + assertTrue(failOnInvalidatedSession); } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java index a163b829140..337510757d8 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java @@ -676,7 +676,7 @@ public void run() { clock.currentTimeMillis += clock.currentTimeMillis + (options.getKeepAliveIntervalMinutes() + 5) * 60 * 1000; session1 = pool.getSession(); - session1.writeAtLeastOnce(new ArrayList()); + session1.writeAtLeastOnceWithOptions(new ArrayList()); session1.close(); runMaintainanceLoop(clock, pool, pool.poolMaintainer.numKeepAliveCycles); // The session pool only keeps MinSessions + MaxIdleSessions alive. @@ -900,8 +900,7 @@ public void testSessionNotFoundReadWriteTransaction() { when(closedSession.newTransaction(Options.fromTransactionOptions())) .thenReturn(closedTransactionContext); when(closedSession.beginTransactionAsync()).thenThrow(sessionNotFound); - TransactionRunnerImpl closedTransactionRunner = - new TransactionRunnerImpl(closedSession, rpc, 10); + TransactionRunnerImpl closedTransactionRunner = new TransactionRunnerImpl(closedSession); closedTransactionRunner.setSpan(mock(Span.class)); when(closedSession.readWriteTransaction()).thenReturn(closedTransactionRunner); @@ -915,8 +914,7 @@ public void testSessionNotFoundReadWriteTransaction() { .thenReturn(openTransactionContext); when(openSession.beginTransactionAsync()) .thenReturn(ApiFutures.immediateFuture(ByteString.copyFromUtf8("open-txn"))); - TransactionRunnerImpl openTransactionRunner = - new TransactionRunnerImpl(openSession, mock(SpannerRpc.class), 10); + TransactionRunnerImpl openTransactionRunner = new TransactionRunnerImpl(openSession); openTransactionRunner.setSpan(mock(Span.class)); when(openSession.readWriteTransaction()).thenReturn(openTransactionRunner); @@ -1047,10 +1045,13 @@ public void testSessionNotFoundWrite() { SpannerExceptionFactoryTest.newSessionNotFoundException(sessionName); List mutations = Arrays.asList(Mutation.newInsertBuilder("FOO").build()); final SessionImpl closedSession = mockSession(); - when(closedSession.write(mutations)).thenThrow(sessionNotFound); + when(closedSession.writeWithOptions(mutations)).thenThrow(sessionNotFound); final SessionImpl openSession = mockSession(); - when(openSession.write(mutations)).thenReturn(Timestamp.now()); + com.google.cloud.spanner.CommitResponse response = + mock(com.google.cloud.spanner.CommitResponse.class); + when(response.getCommitTimestamp()).thenReturn(Timestamp.now()); + when(openSession.writeWithOptions(mutations)).thenReturn(response); doAnswer( new Answer() { @Override @@ -1099,10 +1100,13 @@ public void testSessionNotFoundWriteAtLeastOnce() { SpannerExceptionFactoryTest.newSessionNotFoundException(sessionName); List mutations = Arrays.asList(Mutation.newInsertBuilder("FOO").build()); final SessionImpl closedSession = mockSession(); - when(closedSession.writeAtLeastOnce(mutations)).thenThrow(sessionNotFound); + when(closedSession.writeAtLeastOnceWithOptions(mutations)).thenThrow(sessionNotFound); final SessionImpl openSession = mockSession(); - when(openSession.writeAtLeastOnce(mutations)).thenReturn(Timestamp.now()); + com.google.cloud.spanner.CommitResponse response = + mock(com.google.cloud.spanner.CommitResponse.class); + when(response.getCommitTimestamp()).thenReturn(Timestamp.now()); + when(openSession.writeAtLeastOnceWithOptions(mutations)).thenReturn(response); doAnswer( new Answer() { @Override diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionContextImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionContextImplTest.java index 21f2a59fb39..1ca9e0e6673 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionContextImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionContextImplTest.java @@ -16,14 +16,19 @@ package com.google.cloud.spanner; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyMap; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.google.api.core.ApiFutures; import com.google.cloud.spanner.TransactionRunnerImpl.TransactionContextImpl; import com.google.cloud.spanner.spi.v1.SpannerRpc; import com.google.protobuf.ByteString; import com.google.rpc.Code; import com.google.rpc.Status; +import com.google.spanner.v1.CommitRequest; import com.google.spanner.v1.ExecuteBatchDmlRequest; import com.google.spanner.v1.ExecuteBatchDmlResponse; import java.util.Arrays; @@ -45,6 +50,35 @@ public void batchDmlException() { batchDml(Code.FAILED_PRECONDITION_VALUE); } + @SuppressWarnings("unchecked") + @Test + public void testReturnCommitStats() { + SessionImpl session = mock(SessionImpl.class); + when(session.getName()).thenReturn("test"); + ByteString transactionId = ByteString.copyFromUtf8("test"); + SpannerRpc rpc = mock(SpannerRpc.class); + when(rpc.commitAsync(any(CommitRequest.class), anyMap())) + .thenReturn( + ApiFutures.immediateFuture(com.google.spanner.v1.CommitResponse.getDefaultInstance())); + + try (TransactionContextImpl context = + TransactionContextImpl.newBuilder() + .setSession(session) + .setRpc(rpc) + .setTransactionId(transactionId) + .setOptions(Options.fromTransactionOptions(Options.commitStats())) + .build()) { + context.commitAsync(); + CommitRequest request = + CommitRequest.newBuilder() + .setReturnCommitStats(true) + .setSession(session.getName()) + .setTransactionId(transactionId) + .build(); + verify(rpc).commitAsync(Mockito.eq(request), anyMap()); + } + } + @SuppressWarnings("unchecked") private void batchDml(int status) { SessionImpl session = mock(SessionImpl.class); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java index 39e65e21d91..4ffed254cb6 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java @@ -37,7 +37,6 @@ import com.google.protobuf.Empty; import com.google.spanner.v1.BeginTransactionRequest; import com.google.spanner.v1.CommitRequest; -import com.google.spanner.v1.CommitResponse; import com.google.spanner.v1.ExecuteSqlRequest; import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions; import com.google.spanner.v1.ResultSetMetadata; @@ -141,7 +140,8 @@ public void transactionRolledBackOnClose() { public void commitSucceeds() { when(session.newTransaction(Options.fromTransactionOptions())).thenReturn(txn); Timestamp commitTimestamp = Timestamp.ofTimeMicroseconds(1); - when(txn.commitTimestamp()).thenReturn(commitTimestamp); + CommitResponse response = new CommitResponse(commitTimestamp); + when(txn.getCommitResponse()).thenReturn(response); manager.begin(); manager.commit(); assertThat(manager.getState()).isEqualTo(TransactionState.COMMITTED); @@ -266,12 +266,12 @@ public ApiFuture answer(InvocationOnMock invocation) { }); when(rpc.commitAsync(Mockito.any(CommitRequest.class), Mockito.anyMap())) .thenAnswer( - new Answer>() { + new Answer>() { @Override - public ApiFuture answer(InvocationOnMock invocation) - throws Throwable { + public ApiFuture answer( + InvocationOnMock invocation) throws Throwable { return ApiFutures.immediateFuture( - CommitResponse.newBuilder() + com.google.spanner.v1.CommitResponse.newBuilder() .setCommitTimestamp( com.google.protobuf.Timestamp.newBuilder() .setSeconds(System.currentTimeMillis() * 1000)) @@ -360,12 +360,12 @@ public com.google.spanner.v1.ResultSet answer(InvocationOnMock invocation) }); when(rpc.commitAsync(Mockito.any(CommitRequest.class), Mockito.anyMap())) .thenAnswer( - new Answer>() { + new Answer>() { @Override - public ApiFuture answer(InvocationOnMock invocation) - throws Throwable { + public ApiFuture answer( + InvocationOnMock invocation) throws Throwable { return ApiFutures.immediateFuture( - CommitResponse.newBuilder() + com.google.spanner.v1.CommitResponse.newBuilder() .setCommitTimestamp( com.google.protobuf.Timestamp.newBuilder() .setSeconds(System.currentTimeMillis() * 1000)) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java index fc7dde22f45..b65f70b3845 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java @@ -123,7 +123,7 @@ public ResultSet answer(InvocationOnMock invocation) throws Throwable { return builder.build(); } }); - transactionRunner = new TransactionRunnerImpl(session, rpc, 1); + transactionRunner = new TransactionRunnerImpl(session); when(rpc.commitAsync(Mockito.any(CommitRequest.class), Mockito.anyMap())) .thenReturn( ApiFutures.immediateFuture( @@ -323,7 +323,7 @@ public void prepareReadWriteTransaction() { } }; session.setCurrentSpan(mock(Span.class)); - TransactionRunnerImpl runner = new TransactionRunnerImpl(session, rpc, 10); + TransactionRunnerImpl runner = new TransactionRunnerImpl(session); runner.setSpan(mock(Span.class)); assertThat(usedInlinedBegin).isFalse(); runner.run( @@ -356,7 +356,7 @@ private long[] batchDmlException(int status) { .thenReturn( ApiFutures.immediateFuture(ByteString.copyFromUtf8(UUID.randomUUID().toString()))); when(session.getName()).thenReturn(SessionId.of("p", "i", "d", "test").getName()); - TransactionRunnerImpl runner = new TransactionRunnerImpl(session, rpc, 10); + TransactionRunnerImpl runner = new TransactionRunnerImpl(session); runner.setSpan(mock(Span.class)); ExecuteBatchDmlResponse response1 = ExecuteBatchDmlResponse.newBuilder() diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java index 88f942122a9..113738bf058 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java @@ -37,6 +37,7 @@ import com.google.api.gax.longrunning.OperationFuture; import com.google.cloud.NoCredentials; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.CommitResponse; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.ForwardingResultSet; @@ -84,7 +85,7 @@ public class ConnectionImplTest { static class SimpleTransactionManager implements TransactionManager { private TransactionState state; - private Timestamp commitTimestamp; + private CommitResponse commitResponse; private TransactionContext txContext; private SimpleTransactionManager(TransactionContext txContext) { @@ -99,7 +100,7 @@ public TransactionContext begin() { @Override public void commit() { - commitTimestamp = Timestamp.now(); + commitResponse = new CommitResponse(Timestamp.ofTimeSecondsAndNanos(1, 1)); state = TransactionState.COMMITTED; } @@ -115,7 +116,12 @@ public TransactionContext resetForRetry() { @Override public Timestamp getCommitTimestamp() { - return commitTimestamp; + return commitResponse == null ? null : commitResponse.getCommitTimestamp(); + } + + @Override + public CommitResponse getCommitResponse() { + return commitResponse; } @Override @@ -317,15 +323,15 @@ public Timestamp answer(InvocationOnMock invocation) { public TransactionRunner answer(InvocationOnMock invocation) { TransactionRunner runner = new TransactionRunner() { - private Timestamp commitTimestamp; + private CommitResponse commitResponse; @Override public T run(TransactionCallable callable) { - this.commitTimestamp = Timestamp.now(); - TransactionContext tx = mock(TransactionContext.class); - when(tx.executeUpdate(Statement.of(UPDATE))).thenReturn(1L); + commitResponse = new CommitResponse(Timestamp.ofTimeSecondsAndNanos(1, 1)); + TransactionContext transaction = mock(TransactionContext.class); + when(transaction.executeUpdate(Statement.of(UPDATE))).thenReturn(1L); try { - return callable.run(tx); + return callable.run(transaction); } catch (Exception e) { throw SpannerExceptionFactory.newSpannerException(e); } @@ -333,7 +339,12 @@ public T run(TransactionCallable callable) { @Override public Timestamp getCommitTimestamp() { - return commitTimestamp; + return commitResponse == null ? null : commitResponse.getCommitTimestamp(); + } + + @Override + public CommitResponse getCommitResponse() { + return commitResponse; } @Override diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java index 1e094eaeb6f..de5c9fbdeba 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java @@ -32,6 +32,7 @@ import com.google.cloud.Timestamp; import com.google.cloud.spanner.AbortedException; +import com.google.cloud.spanner.CommitResponse; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.ReadContext.QueryAnalyzeMode; @@ -69,7 +70,7 @@ private enum CommitBehavior { private static class SimpleTransactionManager implements TransactionManager { private TransactionState state; - private Timestamp commitTimestamp; + private CommitResponse commitResponse; private TransactionContext txContext; private CommitBehavior commitBehavior; @@ -88,7 +89,7 @@ public TransactionContext begin() { public void commit() { switch (commitBehavior) { case SUCCEED: - commitTimestamp = Timestamp.now(); + commitResponse = new CommitResponse(Timestamp.ofTimeSecondsAndNanos(1, 1)); state = TransactionState.COMMITTED; break; case FAIL: @@ -115,7 +116,11 @@ public TransactionContext resetForRetry() { @Override public Timestamp getCommitTimestamp() { - return commitTimestamp; + return commitResponse == null ? null : commitResponse.getCommitTimestamp(); + } + + public CommitResponse getCommitResponse() { + return commitResponse; } @Override diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java index 76ef62a21a1..af761d4cbef 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java @@ -29,6 +29,7 @@ import com.google.api.gax.longrunning.OperationFuture; import com.google.cloud.Timestamp; import com.google.cloud.spanner.AsyncResultSet; +import com.google.cloud.spanner.CommitResponse; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.Key; @@ -97,7 +98,7 @@ static StatementTimeout timeout(long timeout, TimeUnit unit) { private static class SimpleTransactionManager implements TransactionManager { private TransactionState state; - private Timestamp commitTimestamp; + private CommitResponse commitResponse; private TransactionContext txContext; private CommitBehavior commitBehavior; @@ -116,7 +117,7 @@ public TransactionContext begin() { public void commit() { switch (commitBehavior) { case SUCCEED: - commitTimestamp = Timestamp.now(); + commitResponse = new CommitResponse(Timestamp.ofTimeSecondsAndNanos(1, 1)); state = TransactionState.COMMITTED; break; case FAIL: @@ -143,7 +144,12 @@ public TransactionContext resetForRetry() { @Override public Timestamp getCommitTimestamp() { - return commitTimestamp; + return commitResponse.getCommitTimestamp(); + } + + @Override + public CommitResponse getCommitResponse() { + return commitResponse; } @Override @@ -387,7 +393,7 @@ public Long answer(InvocationOnMock invocation) throws Throwable { public TransactionRunner answer(InvocationOnMock invocation) { TransactionRunner runner = new TransactionRunner() { - private Timestamp commitTimestamp; + private CommitResponse commitResponse; @Override public T run(TransactionCallable callable) { @@ -398,7 +404,8 @@ public T run(TransactionCallable callable) { } catch (Exception e) { throw SpannerExceptionFactory.newSpannerException(e); } - this.commitTimestamp = Timestamp.now(); + commitResponse = + new CommitResponse(Timestamp.ofTimeSecondsAndNanos(1, 1)); return res; } else if (commitBehavior == CommitBehavior.FAIL) { throw SpannerExceptionFactory.newSpannerException( @@ -411,10 +418,17 @@ public T run(TransactionCallable callable) { @Override public Timestamp getCommitTimestamp() { - if (commitTimestamp == null) { + if (commitResponse == null) { throw new IllegalStateException("no commit timestamp"); } - return commitTimestamp; + return commitResponse.getCommitTimestamp(); + } + + public CommitResponse getCommitResponse() { + if (commitResponse == null) { + throw new IllegalStateException("no commit response"); + } + return commitResponse; } @Override diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java index 721536cb6b3..5bdb685c8bd 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java @@ -16,15 +16,25 @@ package com.google.cloud.spanner.it; +import static com.google.cloud.spanner.SpannerApiFutures.get; +import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; +import com.google.cloud.spanner.AbortedException; import com.google.cloud.spanner.AsyncResultSet; import com.google.cloud.spanner.AsyncResultSet.CallbackResponse; import com.google.cloud.spanner.AsyncResultSet.ReadyCallback; import com.google.cloud.spanner.AsyncRunner; import com.google.cloud.spanner.AsyncRunner.AsyncWork; +import com.google.cloud.spanner.AsyncTransactionManager; +import com.google.cloud.spanner.AsyncTransactionManager.AsyncTransactionFunction; +import com.google.cloud.spanner.AsyncTransactionManager.TransactionContextFuture; import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; @@ -35,6 +45,7 @@ import com.google.cloud.spanner.KeyRange; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Options; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.Struct; @@ -51,6 +62,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; @@ -86,7 +98,17 @@ public static void setUpDatabase() { "CREATE INDEX TestTableByValue ON TestTable(StringValue)", "CREATE INDEX TestTableByValueDesc ON TestTable(StringValue DESC)"); client = env.getTestHelper().getDatabaseClient(db); + executor = Executors.newSingleThreadExecutor(); + } + @AfterClass + public static void cleanup() { + executor.shutdown(); + } + + @Before + public void setupData() { + client.write(Arrays.asList(Mutation.delete(TABLE_NAME, KeySet.all()))); // Includes k0..k14. Note that strings k{10,14} sort between k1 and k2. List mutations = new ArrayList<>(); for (int i = 0; i < 15; ++i) { @@ -99,12 +121,6 @@ public static void setUpDatabase() { .build()); } client.write(mutations); - executor = Executors.newSingleThreadExecutor(); - } - - @AfterClass - public static void cleanup() { - executor.shutdown(); } @Test @@ -302,4 +318,65 @@ public ApiFuture doWorkAsync(TransactionContext txn) { assertThat(client.singleUse().readRow("TestTable", Key.of("k999"), ALL_COLUMNS)).isNull(); } } + + @Test + public void testAsyncRunnerReturnsCommitStats() { + assumeFalse("Emulator does not return commit statistics", isUsingEmulator()); + AsyncRunner runner = client.runAsync(Options.commitStats()); + runner.runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext transaction) { + transaction.buffer( + Mutation.newInsertOrUpdateBuilder(TABLE_NAME) + .set("Key") + .to("k_commit_stats") + .set("StringValue") + .to("Should return commit stats") + .build()); + return ApiFutures.immediateFuture(null); + } + }, + executor); + assertNotNull(get(runner.getCommitResponse()).getCommitStats()); + // MutationCount = 2 columns + 2 secondary indexes. + assertEquals(4L, get(runner.getCommitResponse()).getCommitStats().getMutationCount()); + } + + @Test + public void testAsyncTransactionManagerReturnsCommitStats() throws InterruptedException { + assumeFalse("Emulator does not return commit statistics", isUsingEmulator()); + try (AsyncTransactionManager manager = client.transactionManagerAsync(Options.commitStats())) { + TransactionContextFuture context = manager.beginAsync(); + while (true) { + try { + get( + context + .then( + new AsyncTransactionFunction() { + @Override + public ApiFuture apply(TransactionContext transaction, Void input) + throws Exception { + transaction.buffer( + Mutation.newInsertOrUpdateBuilder(TABLE_NAME) + .set("Key") + .to("k_commit_stats") + .set("StringValue") + .to("Should return commit stats") + .build()); + return ApiFutures.immediateFuture(null); + } + }, + executor) + .commitAsync()); + assertNotNull(get(manager.getCommitResponse()).getCommitStats()); + assertEquals(4L, get(manager.getCommitResponse()).getCommitStats().getMutationCount()); + break; + } catch (AbortedException e) { + Thread.sleep(e.getRetryDelayInMillis()); + context = manager.resetForRetryAsync(); + } + } + } + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerTest.java index 4d65af67ed0..3ea4a067717 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerTest.java @@ -18,6 +18,7 @@ import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; @@ -29,6 +30,7 @@ import com.google.cloud.spanner.Key; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Options; import com.google.cloud.spanner.ParallelIntegrationTest; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.Struct; @@ -212,4 +214,31 @@ public void abortAndRetry() throws InterruptedException { manager2.close(); } } + + @SuppressWarnings("resource") + @Test + public void testTransactionManagerReturnsCommitStats() throws InterruptedException { + assumeFalse("Emulator does not return commit statistics", isUsingEmulator()); + try (TransactionManager manager = client.transactionManager(Options.commitStats())) { + TransactionContext transaction = manager.begin(); + while (true) { + transaction.buffer( + Mutation.newInsertBuilder("T") + .set("K") + .to("KeyCommitStats") + .set("BoolValue") + .to(true) + .build()); + try { + manager.commit(); + assertNotNull(manager.getCommitResponse().getCommitStats()); + assertEquals(2L, manager.getCommitResponse().getCommitStats().getMutationCount()); + break; + } catch (AbortedException e) { + Thread.sleep(e.getRetryDelayInMillis()); + transaction = manager.resetForRetry(); + } + } + } + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java index 503f0ddf908..ed2bc564b06 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java @@ -19,6 +19,8 @@ import static com.google.cloud.spanner.SpannerExceptionFactory.newSpannerException; import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; @@ -33,6 +35,7 @@ import com.google.cloud.spanner.Key; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Options; import com.google.cloud.spanner.ParallelIntegrationTest; import com.google.cloud.spanner.PartitionOptions; import com.google.cloud.spanner.ReadContext; @@ -630,4 +633,23 @@ public Long run(TransactionContext transaction) throws Exception { assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); } } + + @Test + public void testTransactionRunnerReturnsCommitStats() { + assumeFalse("Emulator does not return commit statistics", isUsingEmulator()); + final String key = uniqueKey(); + TransactionRunner runner = client.readWriteTransaction(Options.commitStats()); + runner.run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + transaction.buffer( + Mutation.newInsertBuilder("T").set("K").to(key).set("V").to(0).build()); + return null; + } + }); + assertNotNull(runner.getCommitResponse().getCommitStats()); + // MutationCount = 2 (2 columns). + assertEquals(2L, runner.getCommitResponse().getCommitStats().getMutationCount()); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java index 6f11fc6b438..3cf9d387a15 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java @@ -17,7 +17,9 @@ package com.google.cloud.spanner.it; import static com.google.cloud.spanner.SpannerMatchers.isSpannerException; +import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; @@ -25,6 +27,7 @@ import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.CommitResponse; import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.ErrorCode; @@ -32,6 +35,7 @@ import com.google.cloud.spanner.Key; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Options; import com.google.cloud.spanner.ParallelIntegrationTest; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.SpannerException; @@ -157,6 +161,44 @@ public void writeAtLeastOnce() { assertThat(row.getString(0)).isEqualTo("v1"); } + @Test + public void testWriteReturnsCommitStats() { + assumeFalse("Emulator does not return commit statistics", isUsingEmulator()); + CommitResponse response = + client.writeWithOptions( + Arrays.asList( + Mutation.newInsertOrUpdateBuilder("T") + .set("K") + .to(lastKey = uniqueString()) + .set("StringValue") + .to("v1") + .build()), + Options.commitStats()); + assertNotNull(response); + assertNotNull(response.getCommitTimestamp()); + assertNotNull(response.getCommitStats()); + assertEquals(2L, response.getCommitStats().getMutationCount()); + } + + @Test + public void testWriteAtLeastOnceReturnsCommitStats() { + assumeFalse("Emulator does not return commit statistics", isUsingEmulator()); + CommitResponse response = + client.writeAtLeastOnceWithOptions( + Arrays.asList( + Mutation.newInsertOrUpdateBuilder("T") + .set("K") + .to(lastKey = uniqueString()) + .set("StringValue") + .to("v1") + .build()), + Options.commitStats()); + assertNotNull(response); + assertNotNull(response.getCommitTimestamp()); + assertNotNull(response.getCommitStats()); + assertEquals(2L, response.getCommitStats().getMutationCount()); + } + @Test public void writeAlreadyExists() { client.write( From ab14a5ec2dc2b7e2141305b5326f436eb6eee76f Mon Sep 17 00:00:00 2001 From: Thiago Nunes Date: Wed, 17 Feb 2021 15:32:33 +1100 Subject: [PATCH 40/42] feat!: Point In Time Recovery (PITR) (#452) * feat: exposes new pitr-lite database fields Exposes earliest version time and version retention period fields in the database class. * feat: adds it tests for updating version retention Adds integration tests for updating the version retention period. * feat: adds new create database tests for pitr Adds create database tests for PITR and refactors the integration test class. * chore: refactors tests Separates PITR database tests into 2 files for clarity. * fix: disables pitr-lite tests in emulator The feature is not supported in the emulator currently. * fix: closes result set in test Addresses PR comment. * fix: updates DatabaseInfo equals/hashcode To compare version retention period and earliest version time. * fix: formatting Fixes formatting of the DatabaseInfo * feature: adds test for throttled pitr field Adds test to check for the throttled field in the update database ddl metadata. * fix: explain further the pitr-lite params in docs Adds more explanations to the purpose of the added params for pitr-lite: version_retention_period and earliest_version_time. * feat: adds version time to backups Adds PITR-lite version time to backups. This should make it possible to specify the consistent time for copying the database. * test: adds integration tests for pitr backups * test: adds tests for pitr restore * test: fixes integration test for pitr restore * test: fixes backup unit test * test: fixes npe on pitr backup test * chore: fixes clirr errors * chore: refactors / addresses pr comments * test: fixes the it pitr sad cases tests * test: fixes pitr backup and restore tests * test: skips pitr backup and restore tests This is because the backend for these features is not ready yet. --- .../clirr-ignored-differences.xml | 15 +- .../java/com/google/cloud/spanner/Backup.java | 3 +- .../com/google/cloud/spanner/BackupInfo.java | 33 ++- .../com/google/cloud/spanner/Database.java | 10 +- .../cloud/spanner/DatabaseAdminClient.java | 26 ++ .../spanner/DatabaseAdminClientImpl.java | 30 ++- .../google/cloud/spanner/DatabaseInfo.java | 54 +++- .../spanner/testing/RemoteSpannerHelper.java | 10 + .../spanner/testing/TimestampHelper.java | 41 +++ .../com/google/cloud/spanner/BackupTest.java | 24 +- .../spanner/DatabaseAdminClientImplTest.java | 48 +++- .../spanner/DatabaseAdminClientTest.java | 69 +++-- .../google/cloud/spanner/DatabaseTest.java | 14 +- .../google/cloud/spanner/it/ITBackupTest.java | 51 +--- .../spanner/it/ITPitrBackupAndRestore.java | 244 ++++++++++++++++++ .../spanner/it/ITPitrCreateDatabaseTest.java | 144 +++++++++++ .../spanner/it/ITPitrUpdateDatabaseTest.java | 207 +++++++++++++++ 17 files changed, 935 insertions(+), 88 deletions(-) create mode 100644 google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/TimestampHelper.java create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITPitrBackupAndRestore.java create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITPitrCreateDatabaseTest.java create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITPitrUpdateDatabaseTest.java diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index fcafcb7c3a1..c72dc8c669c 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -453,7 +453,7 @@ com/google/cloud/spanner/TransactionContext com.google.api.core.ApiFuture executeUpdateAsync(com.google.cloud.spanner.Statement) - + 7012 @@ -475,5 +475,16 @@ com/google/cloud/spanner/TransactionRunner com.google.cloud.spanner.CommitResponse getCommitResponse() - + + + + 7013 + com/google/cloud/spanner/BackupInfo$Builder + com.google.cloud.spanner.BackupInfo$Builder setVersionTime(com.google.cloud.Timestamp) + + + 7012 + com/google/cloud/spanner/DatabaseAdminClient + com.google.api.gax.longrunning.OperationFuture createBackup(com.google.cloud.spanner.Backup) + diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Backup.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Backup.java index d49e9f7f50b..a0052cd381a 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Backup.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Backup.java @@ -65,7 +65,7 @@ public OperationFuture create() { getExpireTime() != null, "Cannot create a backup without an expire time"); Preconditions.checkState( getDatabase() != null, "Cannot create a backup without a source database"); - return dbClient.createBackup(instance(), backup(), sourceDatabase(), getExpireTime()); + return dbClient.createBackup(this); } /** @@ -182,6 +182,7 @@ static Backup fromProto( .setState(fromProtoState(proto.getState())) .setSize(proto.getSizeBytes()) .setExpireTime(Timestamp.fromProto(proto.getExpireTime())) + .setVersionTime(Timestamp.fromProto(proto.getVersionTime())) .setDatabase(DatabaseId.of(proto.getDatabase())) .setProto(proto) .build(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BackupInfo.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BackupInfo.java index 5968bcdc213..199e6ae2ae4 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BackupInfo.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BackupInfo.java @@ -18,6 +18,7 @@ import com.google.api.client.util.Preconditions; import com.google.cloud.Timestamp; +import com.google.spanner.admin.database.v1.Database; import java.util.Objects; import javax.annotation.Nullable; @@ -40,6 +41,17 @@ public abstract static class Builder { */ public abstract Builder setExpireTime(Timestamp expireTime); + /** + * Optional for creating a new backup. + * + *

    Specifies the timestamp to have an externally consistent copy of the database. If no + * version time is specified, it will be automatically set to the backup create time. + * + *

    The version time can be as far in the past as specified by the database earliest version + * time (see {@link Database#getEarliestVersionTime()}). + */ + public abstract Builder setVersionTime(Timestamp versionTime); + /** * Required for creating a new backup. * @@ -55,6 +67,7 @@ abstract static class BuilderImpl extends Builder { protected final BackupId id; private State state = State.UNSPECIFIED; private Timestamp expireTime; + private Timestamp versionTime; private DatabaseId database; private long size; private com.google.spanner.admin.database.v1.Backup proto; @@ -67,6 +80,7 @@ abstract static class BuilderImpl extends Builder { this.id = other.id; this.state = other.state; this.expireTime = other.expireTime; + this.versionTime = other.versionTime; this.database = other.database; this.size = other.size; this.proto = other.proto; @@ -84,6 +98,12 @@ public Builder setExpireTime(Timestamp expireTime) { return this; } + @Override + public Builder setVersionTime(Timestamp versionTime) { + this.versionTime = versionTime; + return this; + } + @Override public Builder setDatabase(DatabaseId database) { Preconditions.checkArgument( @@ -119,6 +139,7 @@ public enum State { private final BackupId id; private final State state; private final Timestamp expireTime; + private final Timestamp versionTime; private final DatabaseId database; private final long size; private final com.google.spanner.admin.database.v1.Backup proto; @@ -128,6 +149,7 @@ public enum State { this.state = builder.state; this.size = builder.size; this.expireTime = builder.expireTime; + this.versionTime = builder.versionTime; this.database = builder.database; this.proto = builder.proto; } @@ -157,6 +179,11 @@ public Timestamp getExpireTime() { return expireTime; } + /** Returns the version time of the backup. */ + public Timestamp getVersionTime() { + return versionTime; + } + /** Returns the id of the database that was used to create the backup. */ public DatabaseId getDatabase() { return database; @@ -180,17 +207,19 @@ public boolean equals(Object o) { && state == that.state && size == that.size && Objects.equals(expireTime, that.expireTime) + && Objects.equals(versionTime, that.versionTime) && Objects.equals(database, that.database); } @Override public int hashCode() { - return Objects.hash(id, state, size, expireTime, database); + return Objects.hash(id, state, size, expireTime, versionTime, database); } @Override public String toString() { return String.format( - "Backup[%s, %s, %d, %s, %s]", id.getName(), state, size, expireTime, database); + "Backup[%s, %s, %d, %s, %s, %s]", + id.getName(), state, size, expireTime, versionTime, database); } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Database.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Database.java index 89ee6e875ac..a442ad2399c 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Database.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Database.java @@ -118,8 +118,14 @@ public OperationFuture backup(Backup backup) { Preconditions.checkArgument( backup.getInstanceId().equals(getId().getInstanceId()), "The instance of the backup must be equal to the instance of this database."); + return dbClient.createBackup( - instance(), backup.getId().getBackup(), database(), backup.getExpireTime()); + dbClient + .newBackupBuilder(backup.getId()) + .setDatabase(getId()) + .setExpireTime(backup.getExpireTime()) + .setVersionTime(backup.getVersionTime()) + .build()); } /** @@ -177,6 +183,8 @@ static Database fromProto( .setState(fromProtoState(proto.getState())) .setCreateTime(Timestamp.fromProto(proto.getCreateTime())) .setRestoreInfo(RestoreInfo.fromProtoOrNullIfDefaultInstance(proto.getRestoreInfo())) + .setVersionRetentionPeriod(proto.getVersionRetentionPeriod()) + .setEarliestVersionTime(Timestamp.fromProto(proto.getEarliestVersionTime())) .setProto(proto) .build(); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClient.java index e7a8ed67871..eae1a3cdf4e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClient.java @@ -101,6 +101,32 @@ OperationFuture createBackup( String sourceInstanceId, String backupId, String databaseId, Timestamp expireTime) throws SpannerException; + /** + * Creates a new backup from a database in a Cloud Spanner instance. + * + *

    Example to create a backup. + * + *

    {@code
    +   * BackupId backupId     = BackupId.of("project", "instance", "backup-id");
    +   * DatabaseId databaseId = DatabaseId.of("project", "instance", "database-id");
    +   * Timestamp expireTime  = Timestamp.ofTimeMicroseconds(expireTimeMicros);
    +   * Timestamp versionTime = Timestamp.ofTimeMicroseconds(versionTimeMicros);
    +   *
    +   * Backup backupToCreate = dbAdminClient
    +   *     .newBackupBuilder(backupId)
    +   *     .setDatabase(databaseId)
    +   *     .setExpireTime(expireTime)
    +   *     .setVersionTime(versionTime)
    +   *     .build();
    +   *
    +   * OperationFuture op = dbAdminClient.createBackup(backupToCreate);
    +   * Backup createdBackup = op.get();
    +   * }
    + * + * @param backup the backup to be created + */ + OperationFuture createBackup(Backup backup) throws SpannerException; + /** * Restore a database from a backup. The database that is restored will be created and may not * already exist. diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClientImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClientImpl.java index 28de150caec..a5eed214a4e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClientImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClientImpl.java @@ -114,16 +114,31 @@ public Database apply(Exception e) { public OperationFuture createBackup( String instanceId, String backupId, String databaseId, Timestamp expireTime) throws SpannerException { - com.google.spanner.admin.database.v1.Backup backup = + final Backup backup = + newBackupBuilder(BackupId.of(projectId, instanceId, backupId)) + .setDatabase(DatabaseId.of(projectId, instanceId, databaseId)) + .setExpireTime(expireTime) + .build(); + return createBackup(backup); + } + + @Override + public OperationFuture createBackup(final Backup backup) { + final String instanceId = backup.getInstanceId().getInstance(); + final String databaseId = backup.getDatabase().getDatabase(); + final String backupId = backup.getId().getBackup(); + final com.google.spanner.admin.database.v1.Backup.Builder backupBuilder = com.google.spanner.admin.database.v1.Backup.newBuilder() .setDatabase(getDatabaseName(instanceId, databaseId)) - .setExpireTime(expireTime.toProto()) - .build(); - String instanceName = getInstanceName(instanceId); - OperationFuture - rawOperationFuture = rpc.createBackup(instanceName, backupId, backup); + .setExpireTime(backup.getExpireTime().toProto()); + if (backup.getVersionTime() != null) { + backupBuilder.setVersionTime(backup.getVersionTime().toProto()); + } + final String instanceName = getInstanceName(instanceId); + final OperationFuture + rawOperationFuture = rpc.createBackup(instanceName, backupId, backupBuilder.build()); - return new OperationFutureImpl( + return new OperationFutureImpl<>( rawOperationFuture.getPollingFuture(), rawOperationFuture.getInitialFuture(), new ApiFunction() { @@ -137,6 +152,7 @@ public Backup apply(OperationSnapshot snapshot) { com.google.spanner.admin.database.v1.Backup.newBuilder(proto) .setName(proto.getName()) .setExpireTime(proto.getExpireTime()) + .setVersionTime(proto.getVersionTime()) .setState(proto.getState()) .build(), DatabaseAdminClientImpl.this); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseInfo.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseInfo.java index 5b7faa324e6..5ba9f0aa765 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseInfo.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseInfo.java @@ -30,6 +30,10 @@ public abstract static class Builder { abstract Builder setRestoreInfo(RestoreInfo restoreInfo); + abstract Builder setVersionRetentionPeriod(String versionRetentionPeriod); + + abstract Builder setEarliestVersionTime(Timestamp earliestVersionTime); + abstract Builder setProto(com.google.spanner.admin.database.v1.Database proto); /** Builds the database from this builder. */ @@ -41,6 +45,8 @@ abstract static class BuilderImpl extends Builder { private State state = State.UNSPECIFIED; private Timestamp createTime; private RestoreInfo restoreInfo; + private String versionRetentionPeriod; + private Timestamp earliestVersionTime; private com.google.spanner.admin.database.v1.Database proto; BuilderImpl(DatabaseId id) { @@ -52,6 +58,8 @@ abstract static class BuilderImpl extends Builder { this.state = other.state; this.createTime = other.createTime; this.restoreInfo = other.restoreInfo; + this.versionRetentionPeriod = other.versionRetentionPeriod; + this.earliestVersionTime = other.earliestVersionTime; this.proto = other.proto; } @@ -73,6 +81,18 @@ Builder setRestoreInfo(@Nullable RestoreInfo restoreInfo) { return this; } + @Override + Builder setVersionRetentionPeriod(String versionRetentionPeriod) { + this.versionRetentionPeriod = versionRetentionPeriod; + return this; + } + + @Override + Builder setEarliestVersionTime(Timestamp earliestVersionTime) { + this.earliestVersionTime = earliestVersionTime; + return this; + } + @Override Builder setProto(@Nullable com.google.spanner.admin.database.v1.Database proto) { this.proto = proto; @@ -96,6 +116,8 @@ public enum State { private final State state; private final Timestamp createTime; private final RestoreInfo restoreInfo; + private final String versionRetentionPeriod; + private final Timestamp earliestVersionTime; private final com.google.spanner.admin.database.v1.Database proto; public DatabaseInfo(DatabaseId id, State state) { @@ -103,6 +125,8 @@ public DatabaseInfo(DatabaseId id, State state) { this.state = state; this.createTime = null; this.restoreInfo = null; + this.versionRetentionPeriod = null; + this.earliestVersionTime = null; this.proto = null; } @@ -111,6 +135,8 @@ public DatabaseInfo(DatabaseId id, State state) { this.state = builder.state; this.createTime = builder.createTime; this.restoreInfo = builder.restoreInfo; + this.versionRetentionPeriod = builder.versionRetentionPeriod; + this.earliestVersionTime = builder.earliestVersionTime; this.proto = builder.proto; } @@ -129,6 +155,23 @@ public Timestamp getCreateTime() { return createTime; } + /** + * Returns the version retention period of the database. This is the period for which Cloud + * Spanner retains all versions of data for the database. For instance, if set to 3 days, Cloud + * Spanner will retain data versions that are up to 3 days old. + */ + public String getVersionRetentionPeriod() { + return versionRetentionPeriod; + } + + /** + * Returns the earliest version time of the database. This is the oldest timestamp that can be + * used to read old versions of the data. + */ + public Timestamp getEarliestVersionTime() { + return earliestVersionTime; + } + /** * Returns the {@link RestoreInfo} of the database if any is available, or null if no * {@link RestoreInfo} is available for this database. @@ -154,16 +197,21 @@ public boolean equals(Object o) { return id.equals(that.id) && state == that.state && Objects.equals(createTime, that.createTime) - && Objects.equals(restoreInfo, that.restoreInfo); + && Objects.equals(restoreInfo, that.restoreInfo) + && Objects.equals(versionRetentionPeriod, that.versionRetentionPeriod) + && Objects.equals(earliestVersionTime, that.earliestVersionTime); } @Override public int hashCode() { - return Objects.hash(id, state, createTime, restoreInfo); + return Objects.hash( + id, state, createTime, restoreInfo, versionRetentionPeriod, earliestVersionTime); } @Override public String toString() { - return String.format("Database[%s, %s, %s, %s]", id.getName(), state, createTime, restoreInfo); + return String.format( + "Database[%s, %s, %s, %s, %s, %s]", + id.getName(), state, createTime, restoreInfo, versionRetentionPeriod, earliestVersionTime); } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java index ef979a87060..6d2fe90227c 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java @@ -30,6 +30,7 @@ import java.util.Arrays; import java.util.List; import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; @@ -45,6 +46,8 @@ public class RemoteSpannerHelper { private final InstanceId instanceId; private static int dbSeq; private static int dbPrefix = new Random().nextInt(Integer.MAX_VALUE); + private static final AtomicInteger backupSeq = new AtomicInteger(); + private static int backupPrefix = new Random().nextInt(Integer.MAX_VALUE); private final List dbs = new ArrayList<>(); protected RemoteSpannerHelper(SpannerOptions options, InstanceId instanceId, Spanner client) { @@ -100,6 +103,13 @@ public String getUniqueDatabaseId() { return String.format("testdb_%d_%04d", dbPrefix, dbSeq++); } + /** + * Returns a backup id which is guaranteed to be unique within the context of this environment. + */ + public String getUniqueBackupId() { + return String.format("testbck_%06d_%04d", backupPrefix, backupSeq.incrementAndGet()); + } + /** * Creates a test database defined by {@code statements} in the test instance. A {@code CREATE * DATABASE ...} statement should not be included; an appropriate name will be chosen and the diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/TimestampHelper.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/TimestampHelper.java new file mode 100644 index 00000000000..021adf4dd7f --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/TimestampHelper.java @@ -0,0 +1,41 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner.testing; + +import com.google.cloud.Timestamp; +import java.util.concurrent.TimeUnit; + +public class TimestampHelper { + + public static Timestamp daysAgo(int days) { + return Timestamp.ofTimeMicroseconds( + TimeUnit.MICROSECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS) + - TimeUnit.MICROSECONDS.convert(days, TimeUnit.DAYS)); + } + + public static Timestamp afterDays(int days) { + return Timestamp.ofTimeMicroseconds( + TimeUnit.MICROSECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS) + + TimeUnit.MICROSECONDS.convert(days, TimeUnit.DAYS)); + } + + public static Timestamp afterMinutes(int minutes) { + return Timestamp.ofTimeMicroseconds( + TimeUnit.MICROSECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS) + + TimeUnit.MICROSECONDS.convert(minutes, TimeUnit.MINUTES)); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BackupTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BackupTest.java index 3bdc6738382..80b99f679b4 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BackupTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BackupTest.java @@ -46,6 +46,7 @@ public class BackupTest { "projects/test-project/instances/test-instance/backups/backup-1"; private static final String DB = "projects/test-project/instances/test-instance/databases/db-1"; private static final Timestamp EXP_TIME = Timestamp.ofTimeSecondsAndNanos(1000L, 1000); + private static final Timestamp VERSION_TIME = Timestamp.ofTimeSecondsAndNanos(2000L, 2000); @Mock DatabaseAdminClient dbClient; @@ -65,11 +66,13 @@ public Builder answer(InvocationOnMock invocation) { @Test public void build() { Timestamp expireTime = Timestamp.now(); + Timestamp versionTime = Timestamp.ofTimeMicroseconds(10L); Backup backup = dbClient .newBackupBuilder(BackupId.of("test-project", "instance-id", "backup-id")) .setDatabase(DatabaseId.of("test-project", "instance-id", "src-database")) .setExpireTime(expireTime) + .setVersionTime(versionTime) .setSize(100L) .setState(State.CREATING) .build(); @@ -77,6 +80,7 @@ public void build() { assertThat(copy.getId()).isEqualTo(backup.getId()); assertThat(copy.getDatabase()).isEqualTo(backup.getDatabase()); assertThat(copy.getExpireTime()).isEqualTo(backup.getExpireTime()); + assertThat(copy.getVersionTime()).isEqualTo(backup.getVersionTime()); assertThat(copy.getSize()).isEqualTo(backup.getSize()); assertThat(copy.getState()).isEqualTo(backup.getState()); } @@ -84,14 +88,16 @@ public void build() { @Test public void create() { Timestamp expireTime = Timestamp.now(); + Timestamp versionTime = Timestamp.ofTimeMicroseconds(10L); Backup backup = dbClient .newBackupBuilder(BackupId.of("test-project", "instance-id", "backup-id")) .setDatabase(DatabaseId.of("test-project", "instance-id", "src-database")) .setExpireTime(expireTime) + .setVersionTime(versionTime) .build(); backup.create(); - verify(dbClient).createBackup("instance-id", "backup-id", "src-database", expireTime); + verify(dbClient).createBackup(backup); } @Test @@ -125,6 +131,19 @@ public void createWithoutExpireTime() { } } + @Test + public void createWithoutVersionTimeShouldSucceed() { + final Timestamp expireTime = Timestamp.now(); + Backup backup = + dbClient + .newBackupBuilder(BackupId.of("test-project", "instance-id", "backup-id")) + .setDatabase(DatabaseId.of("test-project", "instance-id", "src-database")) + .setExpireTime(expireTime) + .build(); + backup.create(); + verify(dbClient).createBackup(backup); + } + @Test public void exists() { when(dbClient.getBackup("test-instance", "test-backup")) @@ -298,6 +317,7 @@ public void fromProto() { assertThat(backup.getId().getName()).isEqualTo(NAME); assertThat(backup.getState()).isEqualTo(BackupInfo.State.CREATING); assertThat(backup.getExpireTime()).isEqualTo(EXP_TIME); + assertThat(backup.getVersionTime()).isEqualTo(VERSION_TIME); } private Backup createBackup() { @@ -307,6 +327,8 @@ private Backup createBackup() { .setDatabase(DB) .setExpireTime( com.google.protobuf.Timestamp.newBuilder().setSeconds(1000L).setNanos(1000).build()) + .setVersionTime( + com.google.protobuf.Timestamp.newBuilder().setSeconds(2000L).setNanos(2000).build()) .setState(com.google.spanner.admin.database.v1.Backup.State.CREATING) .build(); return Backup.fromProto(proto, dbClient); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseAdminClientImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseAdminClientImplTest.java index b2676473cf1..fb617797aa0 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseAdminClientImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseAdminClientImplTest.java @@ -48,6 +48,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.junit.Assert; import org.junit.Before; @@ -69,6 +70,8 @@ public class DatabaseAdminClientImplTest { private static final String BK_ID = "my-bk"; private static final String BK_NAME = "projects/my-project/instances/my-instance/backups/my-bk"; private static final String BK_NAME2 = "projects/my-project/instances/my-instance/backups/my-bk2"; + private static final Timestamp EARLIEST_VERSION_TIME = Timestamp.now(); + private static final String VERSION_RETENTION_PERIOD = "7d"; @Mock SpannerRpc rpc; DatabaseAdminClientImpl client; @@ -80,7 +83,12 @@ public void setUp() { } private Database getDatabaseProto() { - return Database.newBuilder().setName(DB_NAME).setState(Database.State.READY).build(); + return Database.newBuilder() + .setName(DB_NAME) + .setState(Database.State.READY) + .setEarliestVersionTime(EARLIEST_VERSION_TIME.toProto()) + .setVersionRetentionPeriod(VERSION_RETENTION_PERIOD) + .build(); } private Database getAnotherDatabaseProto() { @@ -116,6 +124,8 @@ public void getDatabase() { com.google.cloud.spanner.Database db = client.getDatabase(INSTANCE_ID, DB_ID); assertThat(db.getId().getName()).isEqualTo(DB_NAME); assertThat(db.getState()).isEqualTo(DatabaseInfo.State.READY); + assertThat(db.getEarliestVersionTime()).isEqualTo(EARLIEST_VERSION_TIME); + assertThat(db.getVersionRetentionPeriod()).isEqualTo(VERSION_RETENTION_PERIOD); } @Test @@ -292,7 +302,7 @@ public void testDatabaseIAMPermissions() { } @Test - public void createBackup() throws Exception { + public void createBackupWithParams() throws Exception { OperationFuture rawOperationFuture = OperationFutureUtil.immediateOperationFuture( "createBackup", getBackupProto(), CreateBackupMetadata.getDefaultInstance()); @@ -308,6 +318,40 @@ public void createBackup() throws Exception { assertThat(op.get().getId().getName()).isEqualTo(BK_NAME); } + @Test + public void createBackupWithBackupObject() throws ExecutionException, InterruptedException { + final OperationFuture rawOperationFuture = + OperationFutureUtil.immediateOperationFuture( + "createBackup", getBackupProto(), CreateBackupMetadata.getDefaultInstance()); + final Timestamp expireTime = + Timestamp.ofTimeMicroseconds( + TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()) + + TimeUnit.HOURS.toMicros(28)); + final Timestamp versionTime = + Timestamp.ofTimeMicroseconds( + TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()) - TimeUnit.DAYS.toMicros(2)); + final Backup expectedCallBackup = + Backup.newBuilder() + .setDatabase(DB_NAME) + .setExpireTime(expireTime.toProto()) + .setVersionTime(versionTime.toProto()) + .build(); + final com.google.cloud.spanner.Backup requestBackup = + client + .newBackupBuilder(BackupId.of(PROJECT_ID, INSTANCE_ID, BK_ID)) + .setDatabase(DatabaseId.of(PROJECT_ID, INSTANCE_ID, DB_ID)) + .setExpireTime(expireTime) + .setVersionTime(versionTime) + .build(); + + when(rpc.createBackup(INSTANCE_NAME, BK_ID, expectedCallBackup)).thenReturn(rawOperationFuture); + + final OperationFuture op = + client.createBackup(requestBackup); + assertThat(op.isDone()).isTrue(); + assertThat(op.get().getId().getName()).isEqualTo(BK_NAME); + } + @Test public void deleteBackup() { client.deleteBackup(INSTANCE_ID, BK_ID); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseAdminClientTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseAdminClientTest.java index 8b30df912d3..95641ef3fff 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseAdminClientTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseAdminClientTest.java @@ -16,6 +16,7 @@ package com.google.cloud.spanner; +import static com.google.cloud.spanner.testing.TimestampHelper.afterDays; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; @@ -224,7 +225,7 @@ public void tearDown() { public void dbAdminCreateBackup() throws InterruptedException, ExecutionException { final String backupId = "other-backup-id"; OperationFuture op = - client.createBackup(INSTANCE_ID, backupId, DB_ID, after7Days()); + client.createBackup(INSTANCE_ID, backupId, DB_ID, afterDays(7)); Backup backup = op.get(); assertThat(backup.getId().getName()) .isEqualTo( @@ -240,7 +241,8 @@ public void backupCreate() throws InterruptedException, ExecutionException { client .newBackupBuilder(BackupId.of(PROJECT_ID, INSTANCE_ID, backupId)) .setDatabase(DatabaseId.of(PROJECT_ID, INSTANCE_ID, DB_ID)) - .setExpireTime(after7Days()) + .setExpireTime(afterDays(7)) + .setVersionTime(sevenDaysAgo()) .build(); OperationFuture op = backup.create(); backup = op.get(); @@ -251,6 +253,25 @@ public void backupCreate() throws InterruptedException, ExecutionException { assertThat(client.getBackup(INSTANCE_ID, backupId)).isEqualTo(backup); } + @Test + public void databaseAdminBackupCreate() throws ExecutionException, InterruptedException { + final String backupId = "other-backup-id"; + Backup backup = + client + .newBackupBuilder(BackupId.of(PROJECT_ID, INSTANCE_ID, backupId)) + .setDatabase(DatabaseId.of(PROJECT_ID, INSTANCE_ID, DB_ID)) + .setExpireTime(afterDays(7)) + .setVersionTime(sevenDaysAgo()) + .build(); + final OperationFuture op = client.createBackup(backup); + backup = op.get(); + assertThat(backup.getId().getName()) + .isEqualTo( + String.format( + "projects/%s/instances/%s/backups/%s", PROJECT_ID, INSTANCE_ID, backupId)); + assertThat(client.getBackup(INSTANCE_ID, backupId)).isEqualTo(backup); + } + @Test public void backupCreateCancel() { final String backupId = "other-backup-id"; @@ -299,7 +320,7 @@ public void databaseBackup() throws InterruptedException, ExecutionException { db.backup( client .newBackupBuilder(BackupId.of(PROJECT_ID, INSTANCE_ID, backupId)) - .setExpireTime(after7Days()) + .setExpireTime(afterDays(7)) .build()) .get(); assertThat(backup.getId().getName()) @@ -312,7 +333,7 @@ public void databaseBackup() throws InterruptedException, ExecutionException { @Test public void dbAdminCreateBackupAlreadyExists() throws InterruptedException { OperationFuture op = - client.createBackup(INSTANCE_ID, BCK_ID, DB_ID, after7Days()); + client.createBackup(INSTANCE_ID, BCK_ID, DB_ID, afterDays(7)); try { op.get(); fail("missing expected exception"); @@ -329,7 +350,7 @@ public void backupCreateAlreadyExists() throws InterruptedException { client .newBackupBuilder(BackupId.of(PROJECT_ID, INSTANCE_ID, BCK_ID)) .setDatabase(DatabaseId.of(PROJECT_ID, INSTANCE_ID, DB_ID)) - .setExpireTime(after7Days()) + .setExpireTime(afterDays(7)) .build(); try { backup.create().get(); @@ -348,7 +369,7 @@ public void databaseBackupAlreadyExists() throws InterruptedException { db.backup( client .newBackupBuilder(BackupId.of(PROJECT_ID, INSTANCE_ID, BCK_ID)) - .setExpireTime(after7Days()) + .setExpireTime(afterDays(7)) .build()); try { op.get(); @@ -364,7 +385,7 @@ public void databaseBackupAlreadyExists() throws InterruptedException { public void dbAdminCreateBackupDbNotFound() throws InterruptedException { final String backupId = "other-backup-id"; OperationFuture op = - client.createBackup(INSTANCE_ID, backupId, "does-not-exist", after7Days()); + client.createBackup(INSTANCE_ID, backupId, "does-not-exist", afterDays(7)); try { op.get(); fail("missing expected exception"); @@ -381,7 +402,7 @@ public void backupCreateDbNotFound() throws InterruptedException { client .newBackupBuilder(BackupId.of(PROJECT_ID, INSTANCE_ID, backupId)) .setDatabase(DatabaseId.of(PROJECT_ID, INSTANCE_ID, "does-not-exist")) - .setExpireTime(after7Days()) + .setExpireTime(afterDays(7)) .build(); try { backup.create().get(); @@ -402,7 +423,7 @@ public void databaseBackupDbNotFound() throws InterruptedException { db.backup( client .newBackupBuilder(BackupId.of(PROJECT_ID, INSTANCE_ID, backupId)) - .setExpireTime(after7Days()) + .setExpireTime(afterDays(7)) .build()); try { op.get(); @@ -499,7 +520,7 @@ public void dbClientListBackups() throws SpannerException, InterruptedException, ExecutionException { Backup backup = client.getBackup(INSTANCE_ID, BCK_ID); assertThat(client.listBackups(INSTANCE_ID).iterateAll()).containsExactly(backup); - Backup backup2 = client.createBackup(INSTANCE_ID, "backup2", DB_ID, after7Days()).get(); + Backup backup2 = client.createBackup(INSTANCE_ID, "backup2", DB_ID, afterDays(7)).get(); assertThat(client.listBackups(INSTANCE_ID).iterateAll()).containsExactly(backup, backup2); backup2.delete(); assertThat(client.listBackups(INSTANCE_ID).iterateAll()).containsExactly(backup); @@ -515,7 +536,7 @@ public void instanceListBackups() .build(); Backup backup = client.getBackup(INSTANCE_ID, BCK_ID); assertThat(instance.listBackups().iterateAll()).containsExactly(backup); - Backup backup2 = client.createBackup(INSTANCE_ID, "backup2", DB_ID, after7Days()).get(); + Backup backup2 = client.createBackup(INSTANCE_ID, "backup2", DB_ID, afterDays(7)).get(); assertThat(instance.listBackups().iterateAll()).containsExactly(backup, backup2); backup2.delete(); assertThat(instance.listBackups().iterateAll()).containsExactly(backup); @@ -532,7 +553,7 @@ public void instanceListBackupsWithFilter() Backup backup = client.getBackup(INSTANCE_ID, BCK_ID); assertThat(instance.listBackups().iterateAll()).containsExactly(backup); - Backup backup2 = client.createBackup(INSTANCE_ID, "backup2", DB_ID, after7Days()).get(); + Backup backup2 = client.createBackup(INSTANCE_ID, "backup2", DB_ID, afterDays(7)).get(); // All backups. assertThat(instance.listBackups().iterateAll()).containsExactly(backup, backup2); @@ -549,7 +570,7 @@ public void instanceListBackupsWithFilter() .containsExactly(backup, backup2); // All backups that expire before a certain time. - String ts = after14Days().toString(); + String ts = afterDays(14).toString(); filter = String.format("expire_time < \"%s\"", ts); mockDatabaseAdmin.addFilterMatches(filter, backup.getId().getName(), backup2.getId().getName()); assertThat(instance.listBackups(Options.filter(filter)).iterateAll()) @@ -701,7 +722,7 @@ public void databaseListDatabaseOperations() public void dbClientListBackupOperations() throws SpannerException, InterruptedException, ExecutionException { assertThat(client.listBackupOperations(INSTANCE_ID).iterateAll()).hasSize(1); - client.createBackup(INSTANCE_ID, "other-backup", DB_ID, after7Days()).get(); + client.createBackup(INSTANCE_ID, "other-backup", DB_ID, afterDays(7)).get(); assertThat(client.listBackupOperations(INSTANCE_ID).iterateAll()).hasSize(2); // Restore a backup. This creates 2 DATABASE operations: One to restore the database and // one to optimize it. @@ -723,7 +744,7 @@ public void instanceListBackupOperations() .backup( client .newBackupBuilder(BackupId.of(PROJECT_ID, INSTANCE_ID, "other-backup")) - .setExpireTime(after7Days()) + .setExpireTime(afterDays(7)) .build()) .get(); assertThat(instance.listBackupOperations().iterateAll()).hasSize(2); @@ -768,7 +789,7 @@ public void backupListBackupOperations() Backup backup = client.newBackupBuilder(BackupId.of(PROJECT_ID, INSTANCE_ID, BCK_ID)).build(); mockDatabaseAdmin.addFilterMatches("name:backups/" + BCK_ID, createBackupOperation.getName()); assertThat(backup.listBackupOperations().iterateAll()).hasSize(1); - client.createBackup(INSTANCE_ID, "other-backup", DB_ID, after7Days()).get(); + client.createBackup(INSTANCE_ID, "other-backup", DB_ID, afterDays(7)).get(); assertThat(backup.listBackupOperations().iterateAll()).hasSize(1); } @@ -791,16 +812,10 @@ public void testDatabaseIAMPermissions() { assertThat(permissions).containsExactly("spanner.databases.select"); } - private Timestamp after7Days() { - return Timestamp.ofTimeMicroseconds( - TimeUnit.MICROSECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS) - + TimeUnit.MICROSECONDS.convert(7, TimeUnit.DAYS)); - } - - private Timestamp after14Days() { + private Timestamp sevenDaysAgo() { return Timestamp.ofTimeMicroseconds( TimeUnit.MICROSECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS) - + TimeUnit.MICROSECONDS.convert(14, TimeUnit.DAYS)); + - TimeUnit.MICROSECONDS.convert(7, TimeUnit.DAYS)); } private void createTestDatabase() { @@ -814,7 +829,7 @@ private void createTestDatabase() { private void createTestBackup() { try { - createBackupOperation = client.createBackup(INSTANCE_ID, BCK_ID, DB_ID, after7Days()); + createBackupOperation = client.createBackup(INSTANCE_ID, BCK_ID, DB_ID, afterDays(7)); createBackupOperation.get(); } catch (InterruptedException | ExecutionException e) { throw SpannerExceptionFactory.newSpannerException(e); @@ -839,7 +854,7 @@ public void retryCreateBackupSlowResponse() throws Exception { SimulatedExecutionTime.ofException(Status.DEADLINE_EXCEEDED.asRuntimeException())); final String backupId = "other-backup-id"; OperationFuture op = - client.createBackup(INSTANCE_ID, backupId, DB_ID, after7Days()); + client.createBackup(INSTANCE_ID, backupId, DB_ID, afterDays(7)); Backup backup = op.get(); assertThat(backup.getId().getName()) .isEqualTo( @@ -857,7 +872,7 @@ public void retryCreateBackupSlowStartup() throws Exception { SimulatedExecutionTime.ofException(Status.DEADLINE_EXCEEDED.asRuntimeException())); final String backupId = "other-backup-id"; OperationFuture op = - client.createBackup(INSTANCE_ID, backupId, DB_ID, after7Days()); + client.createBackup(INSTANCE_ID, backupId, DB_ID, afterDays(7)); Backup backup = op.get(); assertThat(backup.getId().getName()) .isEqualTo( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseTest.java index 95576150071..7c61720b219 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseTest.java @@ -42,6 +42,9 @@ public class DatabaseTest { private static final String NAME = "projects/test-project/instances/test-instance/databases/database-1"; + private static final Timestamp EARLIEST_VERSION_TIME = Timestamp.now(); + private static final String VERSION_RETENTION_PERIOD = "7d"; + @Mock DatabaseAdminClient dbClient; @Before @@ -61,12 +64,13 @@ public Backup.Builder answer(InvocationOnMock invocation) { public void backup() { Timestamp expireTime = Timestamp.now(); Database db = createDatabase(); - db.backup( + Backup backup = dbClient .newBackupBuilder(BackupId.of("test-project", "test-instance", "test-backup")) .setExpireTime(expireTime) - .build()); - verify(dbClient).createBackup("test-instance", "test-backup", "database-1", expireTime); + .build(); + db.backup(backup); + verify(dbClient).createBackup(backup.toBuilder().setDatabase(db.getId()).build()); } @Test @@ -82,6 +86,8 @@ public void fromProto() { Database db = createDatabase(); assertThat(db.getId().getName()).isEqualTo(NAME); assertThat(db.getState()).isEqualTo(DatabaseInfo.State.CREATING); + assertThat(db.getVersionRetentionPeriod()).isEqualTo(VERSION_RETENTION_PERIOD); + assertThat(db.getEarliestVersionTime()).isEqualTo(EARLIEST_VERSION_TIME); } @Test @@ -119,6 +125,8 @@ private Database createDatabase() { com.google.spanner.admin.database.v1.Database.newBuilder() .setName(NAME) .setState(com.google.spanner.admin.database.v1.Database.State.CREATING) + .setEarliestVersionTime(EARLIEST_VERSION_TIME.toProto()) + .setVersionRetentionPeriod(VERSION_RETENTION_PERIOD) .build(); return Database.fromProto(proto, dbClient); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBackupTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBackupTest.java index 0fbd0702ec5..e9b98871847 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBackupTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBackupTest.java @@ -17,6 +17,9 @@ package com.google.cloud.spanner.it; import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; +import static com.google.cloud.spanner.testing.TimestampHelper.afterDays; +import static com.google.cloud.spanner.testing.TimestampHelper.afterMinutes; +import static com.google.cloud.spanner.testing.TimestampHelper.daysAgo; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; @@ -58,7 +61,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -87,7 +89,6 @@ public class ITBackupTest { private InstanceAdminClient instanceAdminClient; private Instance instance; private RemoteSpannerHelper testHelper; - private final AtomicInteger backupSeq = new AtomicInteger(); private List databases = new ArrayList<>(); private List backups = new ArrayList<>(); private final Random random = new Random(); @@ -188,34 +189,6 @@ private void waitForDbOperations(String backupId) throws InterruptedException { } } - private String getUniqueBackupId() { - return String.format("testbck_%06d_%04d", random.nextInt(1000000), backupSeq.incrementAndGet()); - } - - private static Timestamp after7Days() { - return Timestamp.ofTimeMicroseconds( - TimeUnit.MICROSECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS) - + TimeUnit.MICROSECONDS.convert(7L, TimeUnit.DAYS)); - } - - private Timestamp after5Minutes() { - return Timestamp.ofTimeMicroseconds( - TimeUnit.MICROSECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS) - + TimeUnit.MICROSECONDS.convert(5L, TimeUnit.MINUTES)); - } - - private Timestamp tomorrow() { - return Timestamp.ofTimeMicroseconds( - TimeUnit.MICROSECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS) - + TimeUnit.MICROSECONDS.convert(1L, TimeUnit.DAYS)); - } - - private Timestamp yesterday() { - return Timestamp.ofTimeMicroseconds( - TimeUnit.MICROSECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS) - - TimeUnit.MICROSECONDS.convert(1L, TimeUnit.DAYS)); - } - @Test public void testBackups() throws InterruptedException, ExecutionException { // Create two test databases in parallel. @@ -250,9 +223,9 @@ public void testBackups() throws InterruptedException, ExecutionException { .build())); // Create two backups in parallel. - String backupId1 = getUniqueBackupId() + "_bck1"; - String backupId2 = getUniqueBackupId() + "_bck2"; - Timestamp expireTime = after7Days(); + String backupId1 = testHelper.getUniqueBackupId() + "_bck1"; + String backupId2 = testHelper.getUniqueBackupId() + "_bck2"; + Timestamp expireTime = afterDays(7); logger.info(String.format("Creating backups %s and %s in parallel", backupId1, backupId2)); OperationFuture op1 = dbAdminClient.createBackup( @@ -414,8 +387,8 @@ private void testMetadata( private void testCreateInvalidExpirationDate(Database db) throws InterruptedException { // This is not allowed, the expiration date must be at least 6 hours in the future. - Timestamp expireTime = yesterday(); - String backupId = getUniqueBackupId(); + Timestamp expireTime = daysAgo(1); + String backupId = testHelper.getUniqueBackupId(); logger.info(String.format("Creating backup %s with invalid expiration date", backupId)); OperationFuture op = dbAdminClient.createBackup( @@ -437,8 +410,8 @@ private void testCreateInvalidExpirationDate(Database db) throws InterruptedExce private void testCancelBackupOperation(Database db) throws InterruptedException, ExecutionException { - Timestamp expireTime = after7Days(); - String backupId = getUniqueBackupId(); + Timestamp expireTime = afterDays(7); + String backupId = testHelper.getUniqueBackupId(); logger.info(String.format("Starting to create backup %s", backupId)); OperationFuture op = dbAdminClient.createBackup( @@ -477,7 +450,7 @@ private void testGetBackup(Database db, String backupId, Timestamp expireTime) { private void testUpdateBackup(Backup backup) { // Update the expire time. - Timestamp tomorrow = tomorrow(); + Timestamp tomorrow = afterDays(1); backup = backup.toBuilder().setExpireTime(tomorrow).build(); logger.info( String.format("Updating expire time of backup %s to 1 week", backup.getId().getBackup())); @@ -488,7 +461,7 @@ private void testUpdateBackup(Backup backup) { assertThat(backup.getExpireTime()).isEqualTo(tomorrow); // Try to set the expire time to 5 minutes in the future. - Timestamp in5Minutes = after5Minutes(); + Timestamp in5Minutes = afterMinutes(5); backup = backup.toBuilder().setExpireTime(in5Minutes).build(); try { logger.info( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITPitrBackupAndRestore.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITPitrBackupAndRestore.java new file mode 100644 index 00000000000..bfdaa60a258 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITPitrBackupAndRestore.java @@ -0,0 +1,244 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner.it; + +import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; +import static com.google.cloud.spanner.testing.TimestampHelper.afterDays; +import static com.google.cloud.spanner.testing.TimestampHelper.daysAgo; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeFalse; + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.api.gax.paging.Page; +import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Backup; +import com.google.cloud.spanner.BackupId; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.InstanceId; +import com.google.cloud.spanner.IntegrationTestEnv; +import com.google.cloud.spanner.ParallelIntegrationTest; +import com.google.cloud.spanner.SpannerException; +import com.google.cloud.spanner.testing.RemoteSpannerHelper; +import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; +import com.google.spanner.admin.database.v1.RestoreDatabaseMetadata; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@Category(ParallelIntegrationTest.class) +@RunWith(JUnit4.class) +public class ITPitrBackupAndRestore { + private static final Logger logger = Logger.getLogger(ITPitrBackupAndRestore.class.getName()); + + @ClassRule public static IntegrationTestEnv env = new IntegrationTestEnv(); + private static final long OP_TIMEOUT = 10; + private static final TimeUnit OP_TIMEOUT_UNIT = TimeUnit.MINUTES; + private static RemoteSpannerHelper testHelper; + private static DatabaseAdminClient dbAdminClient; + private static Database testDatabase; + private static final List backupsToDrop = new ArrayList<>(); + private static final List databasesToDrop = new ArrayList<>(); + + @BeforeClass + public static void doNotRunOnEmulator() { + assumeFalse("PITR features are not supported by the emulator", isUsingEmulator()); + } + + @BeforeClass + public static void setUp() throws Exception { + testHelper = env.getTestHelper(); + dbAdminClient = testHelper.getClient().getDatabaseAdminClient(); + testDatabase = createTestDatabase(); + } + + @AfterClass + public static void tearDown() { + int numDropped = 0; + for (Database database : databasesToDrop) { + try { + database.drop(); + numDropped++; + } catch (SpannerException e) { + logger.log(Level.SEVERE, "Failed to drop test database " + database.getId(), e); + } + } + logger.log(Level.INFO, "Dropped {0} test databases(s)", numDropped); + + numDropped = 0; + for (Backup backup : backupsToDrop) { + try { + backup.delete(); + numDropped++; + } catch (SpannerException e) { + logger.log(Level.SEVERE, "Failed to drop test backup " + backup.getId(), e); + } + } + logger.log(Level.INFO, "Dropped {0} test backup(s)", numDropped); + } + + @Test + @Ignore("backup and restore for pitr is not released yet") + public void backupCreationWithVersionTimeWithinVersionRetentionPeriodSucceeds() throws Exception { + final DatabaseId backupDatabaseId = testDatabase.getId(); + final String restoreDatabaseId = testHelper.getUniqueDatabaseId(); + final String projectId = backupDatabaseId.getInstanceId().getProject(); + final String instanceId = backupDatabaseId.getInstanceId().getInstance(); + final String backupId = testHelper.getUniqueBackupId(); + final Timestamp expireTime = afterDays(7); + final Timestamp versionTime = testDatabase.getEarliestVersionTime(); + final Backup backupToCreate = + dbAdminClient + .newBackupBuilder(BackupId.of(projectId, instanceId, backupId)) + .setDatabase(backupDatabaseId) + .setExpireTime(expireTime) + .setVersionTime(versionTime) + .build(); + + final Backup createdBackup = createBackup(backupToCreate); + assertThat(createdBackup.getVersionTime()).isEqualTo(versionTime); + + final RestoreDatabaseMetadata restoreDatabaseMetadata = + restoreDatabase(instanceId, backupId, restoreDatabaseId); + assertThat(Timestamp.fromProto(restoreDatabaseMetadata.getBackupInfo().getVersionTime())) + .isEqualTo(versionTime); + + final Database retrievedDatabase = dbAdminClient.getDatabase(instanceId, restoreDatabaseId); + assertThat(retrievedDatabase).isNotNull(); + assertThat( + Timestamp.fromProto( + retrievedDatabase.getRestoreInfo().getProto().getBackupInfo().getVersionTime())) + .isEqualTo(versionTime); + + final Database listedDatabase = listDatabase(instanceId, restoreDatabaseId); + assertThat(listedDatabase).isNotNull(); + assertThat( + Timestamp.fromProto( + listedDatabase.getRestoreInfo().getProto().getBackupInfo().getVersionTime())) + .isEqualTo(versionTime); + } + + @Test(expected = SpannerException.class) + @Ignore("backup and restore for pitr is not released yet") + public void backupCreationWithVersionTimeTooFarInThePastFails() throws Exception { + final DatabaseId databaseId = testDatabase.getId(); + final InstanceId instanceId = databaseId.getInstanceId(); + final String backupId = testHelper.getUniqueBackupId(); + final Timestamp expireTime = afterDays(7); + final Timestamp versionTime = daysAgo(30); + final Backup backupToCreate = + dbAdminClient + .newBackupBuilder(BackupId.of(instanceId, backupId)) + .setDatabase(databaseId) + .setExpireTime(expireTime) + .setVersionTime(versionTime) + .build(); + + createBackup(backupToCreate); + } + + @Test(expected = SpannerException.class) + @Ignore("backup and restore for pitr is not released yet") + public void backupCreationWithVersionTimeInTheFutureFails() throws Exception { + final DatabaseId databaseId = testDatabase.getId(); + final InstanceId instanceId = databaseId.getInstanceId(); + final String backupId = testHelper.getUniqueBackupId(); + final Timestamp expireTime = afterDays(7); + final Timestamp versionTime = afterDays(1); + final Backup backupToCreate = + dbAdminClient + .newBackupBuilder(BackupId.of(instanceId, backupId)) + .setDatabase(databaseId) + .setExpireTime(expireTime) + .setVersionTime(versionTime) + .build(); + + createBackup(backupToCreate); + } + + private Backup createBackup(Backup backupToCreate) + throws InterruptedException, ExecutionException, TimeoutException { + final Backup createdBackup = getOrThrow(dbAdminClient.createBackup(backupToCreate)); + backupsToDrop.add(createdBackup); + return createdBackup; + } + + private RestoreDatabaseMetadata restoreDatabase( + String instanceId, String backupId, String databaseId) + throws InterruptedException, ExecutionException, TimeoutException { + final OperationFuture op = + dbAdminClient.restoreDatabase(instanceId, backupId, instanceId, databaseId); + final Database database = getOrThrow(op); + databasesToDrop.add(database); + return op.getMetadata().get(OP_TIMEOUT, OP_TIMEOUT_UNIT); + } + + private Database listDatabase(String instanceId, String databaseId) { + Page page = dbAdminClient.listDatabases(instanceId); + while (page != null) { + for (Database database : page.getValues()) { + if (database.getId().getDatabase().equals(databaseId)) { + return database; + } + } + page = page.getNextPage(); + } + return null; + } + + private static Database createTestDatabase() + throws InterruptedException, ExecutionException, TimeoutException { + final String instanceId = testHelper.getInstanceId().getInstance(); + final String databaseId = testHelper.getUniqueDatabaseId(); + final OperationFuture op = + dbAdminClient.createDatabase( + instanceId, + databaseId, + Collections.singletonList( + "ALTER DATABASE " + databaseId + " SET OPTIONS (version_retention_period = '7d')")); + final Database database = getOrThrow(op); + databasesToDrop.add(database); + return database; + } + + private static T getOrThrow(OperationFuture op) + throws TimeoutException, InterruptedException, ExecutionException { + try { + return op.get(OP_TIMEOUT, OP_TIMEOUT_UNIT); + } catch (ExecutionException e) { + if (e.getCause() != null && e.getCause() instanceof SpannerException) { + throw (SpannerException) e.getCause(); + } else { + throw e; + } + } + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITPitrCreateDatabaseTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITPitrCreateDatabaseTest.java new file mode 100644 index 00000000000..f9fa081e413 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITPitrCreateDatabaseTest.java @@ -0,0 +1,144 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner.it; + +import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; + +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.DatabaseNotFoundException; +import com.google.cloud.spanner.ErrorCode; +import com.google.cloud.spanner.IntegrationTestEnv; +import com.google.cloud.spanner.ParallelIntegrationTest; +import com.google.cloud.spanner.SpannerException; +import com.google.cloud.spanner.testing.RemoteSpannerHelper; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.threeten.bp.Duration; + +@Category(ParallelIntegrationTest.class) +@RunWith(JUnit4.class) +public class ITPitrCreateDatabaseTest { + + private static final Duration OPERATION_TIMEOUT = Duration.ofMinutes(2); + private static final String VERSION_RETENTION_PERIOD = "7d"; + + @ClassRule public static IntegrationTestEnv env = new IntegrationTestEnv(); + private RemoteSpannerHelper testHelper; + private DatabaseAdminClient dbAdminClient; + private List databasesToDrop; + + @BeforeClass + public static void doNotRunOnEmulator() { + assumeFalse("PITR-lite features are not supported by the emulator", isUsingEmulator()); + } + + @Before + public void setUp() { + testHelper = env.getTestHelper(); + dbAdminClient = testHelper.getClient().getDatabaseAdminClient(); + databasesToDrop = new ArrayList<>(); + } + + @After + public void tearDown() { + for (Database database : databasesToDrop) { + final DatabaseId id = database.getId(); + dbAdminClient.dropDatabase(id.getInstanceId().getInstance(), id.getDatabase()); + } + } + + @Test + public void returnsTheVersionRetentionPeriodSetThroughCreateDatabase() throws Exception { + final String instanceId = testHelper.getInstanceId().getInstance(); + final String databaseId = testHelper.getUniqueDatabaseId(); + final String extraStatement = + "ALTER DATABASE " + + databaseId + + " SET OPTIONS (version_retention_period = '" + + VERSION_RETENTION_PERIOD + + "')"; + + final Database database = createDatabase(instanceId, databaseId, extraStatement); + + assertThat(database.getVersionRetentionPeriod()).isEqualTo(VERSION_RETENTION_PERIOD); + assertThat(database.getEarliestVersionTime()).isNotNull(); + } + + @Test + public void returnsTheVersionRetentionPeriodSetThroughGetDatabase() throws Exception { + final String instanceId = testHelper.getInstanceId().getInstance(); + final String databaseId = testHelper.getUniqueDatabaseId(); + final String extraStatement = + "ALTER DATABASE " + + databaseId + + " SET OPTIONS (version_retention_period = '" + + VERSION_RETENTION_PERIOD + + "')"; + + createDatabase(instanceId, databaseId, extraStatement); + final Database database = dbAdminClient.getDatabase(instanceId, databaseId); + + assertThat(database.getVersionRetentionPeriod()).isEqualTo(VERSION_RETENTION_PERIOD); + assertThat(database.getEarliestVersionTime()).isNotNull(); + } + + @Test(expected = DatabaseNotFoundException.class) + public void returnsAnErrorWhenAnInvalidVersionRetentionPeriodIsGiven() { + final String instanceId = testHelper.getInstanceId().getInstance(); + final String databaseId = testHelper.getUniqueDatabaseId(); + final String extraStatement = + "ALTER DATABASE " + databaseId + " SET OPTIONS (version_retention_period = '0d')"; + + try { + createDatabase(instanceId, databaseId, extraStatement); + fail("Expected invalid argument error when setting invalid version retention period"); + } catch (Exception e) { + SpannerException spannerException = (SpannerException) e.getCause(); + assertThat(spannerException.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + } + + // Expects a database not found exception + dbAdminClient.getDatabase(instanceId, databaseId); + } + + private Database createDatabase( + final String instanceId, final String databaseId, final String extraStatement) + throws Exception { + final Database database = + dbAdminClient + .createDatabase(instanceId, databaseId, Collections.singletonList(extraStatement)) + .get(OPERATION_TIMEOUT.toNanos(), TimeUnit.NANOSECONDS); + databasesToDrop.add(database); + + return database; + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITPitrUpdateDatabaseTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITPitrUpdateDatabaseTest.java new file mode 100644 index 00000000000..72a52b39078 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITPitrUpdateDatabaseTest.java @@ -0,0 +1,207 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner.it; + +import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.api.gax.paging.Page; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.ErrorCode; +import com.google.cloud.spanner.IntegrationTestEnv; +import com.google.cloud.spanner.ParallelIntegrationTest; +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.SpannerException; +import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.testing.RemoteSpannerHelper; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.threeten.bp.Duration; + +@Category(ParallelIntegrationTest.class) +@RunWith(JUnit4.class) +public class ITPitrUpdateDatabaseTest { + + private static final Duration OPERATION_TIMEOUT = Duration.ofMinutes(2); + private static final String VERSION_RETENTION_PERIOD = "7d"; + + @ClassRule public static IntegrationTestEnv env = new IntegrationTestEnv(); + private static DatabaseAdminClient dbAdminClient; + private static DatabaseClient dbClient; + private static String instanceId; + private static String databaseId; + private static UpdateDatabaseDdlMetadata metadata; + + @BeforeClass + public static void setUp() throws Exception { + assumeFalse("PITR-lite features are not supported by the emulator", isUsingEmulator()); + + final RemoteSpannerHelper testHelper = env.getTestHelper(); + final String projectId = testHelper.getOptions().getProjectId(); + instanceId = testHelper.getInstanceId().getInstance(); + databaseId = testHelper.getUniqueDatabaseId(); + dbAdminClient = testHelper.getClient().getDatabaseAdminClient(); + + createDatabase(dbAdminClient, instanceId, databaseId, Collections.emptyList()); + metadata = + updateVersionRetentionPeriod( + dbAdminClient, instanceId, databaseId, VERSION_RETENTION_PERIOD); + + dbClient = + testHelper.getClient().getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + } + + @AfterClass + public static void tearDown() { + if (!isUsingEmulator()) { + dbAdminClient.dropDatabase(instanceId, databaseId); + } + } + + @Test + public void checksThatTheOperationWasNotThrottled() { + assertThat(metadata.getThrottled()).isFalse(); + } + + @Test + public void returnsTheVersionRetentionPeriodSetThroughGetDatabase() { + final Database database = dbAdminClient.getDatabase(instanceId, databaseId); + + assertThat(database.getVersionRetentionPeriod()).isEqualTo(VERSION_RETENTION_PERIOD); + assertThat(database.getEarliestVersionTime()).isNotNull(); + } + + @Test + public void returnsTheVersionRetentionPeriodSetThroughListDatabases() { + final Page page = dbAdminClient.listDatabases(instanceId); + + for (Database database : page.iterateAll()) { + if (!database.getId().getDatabase().equals(databaseId)) { + continue; + } + assertThat(database.getVersionRetentionPeriod()).isEqualTo(VERSION_RETENTION_PERIOD); + assertThat(database.getEarliestVersionTime()).isNotNull(); + } + } + + @Test + public void returnsTheVersionRetentionPeriodSetThroughGetDatabaseDdl() { + final List ddls = dbAdminClient.getDatabaseDdl(instanceId, databaseId); + + boolean hasVersionRetentionPeriodStatement = false; + for (String ddl : ddls) { + hasVersionRetentionPeriodStatement = + ddl.contains("version_retention_period = '" + VERSION_RETENTION_PERIOD + "'"); + if (hasVersionRetentionPeriodStatement) { + break; + } + } + assertThat(hasVersionRetentionPeriodStatement).isTrue(); + } + + @Test + public void returnsTheVersionRetentionPeriodSetThroughInformationSchema() { + try (final ResultSet rs = + dbClient + .singleUse() + .executeQuery( + Statement.of( + "SELECT OPTION_VALUE AS version_retention_period " + + "FROM INFORMATION_SCHEMA.DATABASE_OPTIONS " + + "WHERE SCHEMA_NAME = '' AND OPTION_NAME = 'version_retention_period'"))) { + + String versionRetentionPeriod = null; + while (rs.next()) { + versionRetentionPeriod = rs.getString("version_retention_period"); + } + + assertThat(versionRetentionPeriod).isEqualTo(VERSION_RETENTION_PERIOD); + } + } + + @Test + public void returnsAnErrorWhenAnInvalidRetentionPeriodIsGiven() { + try { + dbAdminClient + .updateDatabaseDdl( + instanceId, + databaseId, + Collections.singletonList( + "ALTER DATABASE " + + databaseId + + " SET OPTIONS (version_retention_period = '0d')"), + "op_invalid_retention_period_" + databaseId) + .get(OPERATION_TIMEOUT.toNanos(), TimeUnit.NANOSECONDS); + fail("Expected invalid argument error when setting invalid version retention period"); + } catch (Exception e) { + SpannerException spannerException = (SpannerException) e.getCause(); + assertThat(spannerException.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + } + + final Database database = dbAdminClient.getDatabase(instanceId, databaseId); + + assertThat(database.getVersionRetentionPeriod()).isEqualTo(VERSION_RETENTION_PERIOD); + assertThat(database.getEarliestVersionTime()).isNotNull(); + } + + private static Database createDatabase( + final DatabaseAdminClient dbAdminClient, + final String instanceId, + final String databaseId, + final Iterable extraStatements) + throws Exception { + return dbAdminClient + .createDatabase(instanceId, databaseId, extraStatements) + .get(OPERATION_TIMEOUT.toNanos(), TimeUnit.NANOSECONDS); + } + + private static UpdateDatabaseDdlMetadata updateVersionRetentionPeriod( + final DatabaseAdminClient dbAdminClient, + final String instanceId, + final String databaseId, + final String versionRetentionPeriod) + throws Exception { + final OperationFuture op = + dbAdminClient.updateDatabaseDdl( + instanceId, + databaseId, + Collections.singletonList( + "ALTER DATABASE " + + databaseId + + " SET OPTIONS ( version_retention_period = '" + + versionRetentionPeriod + + "' )"), + "updateddl_version_retention_period"); + op.get(OPERATION_TIMEOUT.toNanos(), TimeUnit.NANOSECONDS); + return op.getMetadata().get(OPERATION_TIMEOUT.toNanos(), TimeUnit.NANOSECONDS); + } +} From 3de7e2a91349cac5d79a32d2cda7ca727140f0bf Mon Sep 17 00:00:00 2001 From: Thiago Nunes Date: Wed, 17 Feb 2021 15:33:08 +1100 Subject: [PATCH 41/42] fix: allows user-agent header with header provider (#871) * fix: allows user-agent header with header provider A bug was introduced, where if the caller tried to set a custom user agent with a header provider an exception would be thrown (for duplicate keys). Here, we merge the user agent set by the client along with the one set by the library, instead of throwing such exception. * test: adds test for default user agent Tests if the default user agent is present in the user-agent header set in the GapicSpannerRpc class. --- .../cloud/spanner/spi/v1/GapicSpannerRpc.java | 29 +++++++----- .../spanner/spi/v1/GapicSpannerRpcTest.java | 46 +++++++++++++++++++ 2 files changed, 63 insertions(+), 12 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java index 807eac6f11b..608d9e23d53 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java @@ -77,7 +77,6 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.RateLimiter; import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -161,6 +160,7 @@ import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.Comparator; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -244,6 +244,8 @@ private void awaitTermination() throws InterruptedException { private static final int GRPC_KEEPALIVE_SECONDS = 2 * 60; private static final String USER_AGENT_KEY = "user-agent"; private static final String CLIENT_LIBRARY_LANGUAGE = "spanner-java"; + public static final String DEFAULT_USER_AGENT = + CLIENT_LIBRARY_LANGUAGE + "/" + GaxProperties.getLibraryVersion(GapicSpannerRpc.class); private final ManagedInstantiatingExecutorProvider executorProvider; private boolean rpcIsClosed; @@ -305,18 +307,11 @@ public GapicSpannerRpc(final SpannerOptions options) { GaxGrpcProperties.getGrpcTokenName(), GaxGrpcProperties.getGrpcVersion()) .build(); - HeaderProvider mergedHeaderProvider = options.getMergedHeaderProvider(internalHeaderProvider); - Map headersWithUserAgent = - ImmutableMap.builder() - .put( - USER_AGENT_KEY, - CLIENT_LIBRARY_LANGUAGE - + "/" - + GaxProperties.getLibraryVersion(GapicSpannerRpc.class)) - .putAll(mergedHeaderProvider.getHeaders()) - .build(); + final HeaderProvider mergedHeaderProvider = + options.getMergedHeaderProvider(internalHeaderProvider); final HeaderProvider headerProviderWithUserAgent = - FixedHeaderProvider.create(headersWithUserAgent); + headerProviderWithUserAgentFrom(mergedHeaderProvider); + this.metadataProvider = SpannerMetadataProvider.create( headerProviderWithUserAgent.getHeaders(), @@ -494,6 +489,16 @@ public UnaryCallable createUnaryCalla } } + private static HeaderProvider headerProviderWithUserAgentFrom(HeaderProvider headerProvider) { + final Map headersWithUserAgent = new HashMap<>(headerProvider.getHeaders()); + final String userAgent = headersWithUserAgent.get(USER_AGENT_KEY); + headersWithUserAgent.put( + USER_AGENT_KEY, + userAgent == null ? DEFAULT_USER_AGENT : userAgent + " " + DEFAULT_USER_AGENT); + + return FixedHeaderProvider.create(headersWithUserAgent); + } + private static void checkEmulatorConnection( SpannerOptions options, TransportChannelProvider channelProvider, diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpcTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpcTest.java index 84aaa91bcf5..5a9dec72edc 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpcTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpcTest.java @@ -24,7 +24,9 @@ import static org.junit.Assume.assumeTrue; import com.google.api.core.ApiFunction; +import com.google.api.gax.core.GaxProperties; import com.google.api.gax.rpc.ApiCallContext; +import com.google.api.gax.rpc.HeaderProvider; import com.google.auth.oauth2.AccessToken; import com.google.auth.oauth2.OAuth2Credentials; import com.google.cloud.spanner.DatabaseAdminClient; @@ -151,6 +153,8 @@ public class GapicSpannerRpcTest { private Server server; private InetSocketAddress address; private final Map optionsMap = new HashMap<>(); + private Metadata seenHeaders; + private String defaultUserAgent; @BeforeClass public static void checkNotEmulator() { @@ -161,6 +165,7 @@ public static void checkNotEmulator() { @Before public void startServer() throws IOException { + defaultUserAgent = "spanner-java/" + GaxProperties.getLibraryVersion(GapicSpannerRpc.class); mockSpanner = new MockSpannerServiceImpl(); mockSpanner.setAbortProbability(0.0D); // We don't want any unpredictable aborted transactions. mockSpanner.putStatementResult(StatementResult.query(SELECT1AND2, SELECT1_RESULTSET)); @@ -183,6 +188,7 @@ public ServerCall.Listener interceptCall( ServerCall call, Metadata headers, ServerCallHandler next) { + seenHeaders = headers; String auth = headers.get(Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER)); assertThat(auth).isEqualTo("Bearer " + VARIABLE_OAUTH_TOKEN); @@ -502,6 +508,46 @@ public void testAdminRequestsLimitExceededRetryAlgorithm() { assertThat(alg.shouldRetry(new Exception("random exception"), null)).isFalse(); } + @Test + public void testDefaultUserAgent() { + final SpannerOptions options = createSpannerOptions(); + final Spanner spanner = options.getService(); + final DatabaseClient databaseClient = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + + try (final ResultSet rs = databaseClient.singleUse().executeQuery(SELECT1AND2)) { + rs.next(); + } + + assertThat(seenHeaders.get(Key.of("user-agent", Metadata.ASCII_STRING_MARSHALLER))) + .contains(defaultUserAgent); + } + + @Test + public void testCustomUserAgent() { + final HeaderProvider userAgentHeaderProvider = + new HeaderProvider() { + @Override + public Map getHeaders() { + final Map headers = new HashMap<>(); + headers.put("user-agent", "test-agent"); + return headers; + } + }; + final SpannerOptions options = + createSpannerOptions().toBuilder().setHeaderProvider(userAgentHeaderProvider).build(); + final Spanner spanner = options.getService(); + final DatabaseClient databaseClient = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]")); + + try (final ResultSet rs = databaseClient.singleUse().executeQuery(SELECT1AND2)) { + rs.next(); + } + + assertThat(seenHeaders.get(Key.of("user-agent", Metadata.ASCII_STRING_MARSHALLER))) + .contains("test-agent " + defaultUserAgent); + } + @SuppressWarnings("rawtypes") private SpannerOptions createSpannerOptions() { String endpoint = address.getHostString() + ":" + server.getPort(); From 7f73f952f0c5a492a4b19116cf4e129ff9c62105 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Wed, 17 Feb 2021 16:24:45 +1100 Subject: [PATCH 42/42] chore(master): release 4.0.0 (#850) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 33 +++++++++++++++++++ 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, 72 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bb95f58712..aaa57c3d4ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,38 @@ # Changelog +## [4.0.0](https://www.github.com/googleapis/java-spanner/compare/v3.3.2...v4.0.0) (2021-02-17) + + +### ⚠ BREAKING CHANGES + +* Point In Time Recovery (PITR) (#452) +* add support for CommitStats (#544) + +### Features + +* add option for returning Spanner commit stats ([#817](https://www.github.com/googleapis/java-spanner/issues/817)) ([80d3585](https://www.github.com/googleapis/java-spanner/commit/80d3585870b81949ec641291e5a88fe391f78e27)) +* add support for CommitStats ([#544](https://www.github.com/googleapis/java-spanner/issues/544)) ([44aa384](https://www.github.com/googleapis/java-spanner/commit/44aa384429056dd6c6563351c43fe7dcac451008)) +* allow session pool settings in connection url ([#821](https://www.github.com/googleapis/java-spanner/issues/821)) ([e1e9152](https://www.github.com/googleapis/java-spanner/commit/e1e915289755e5f46ba07569d85afda5df5e3f0d)) +* generate sample code in the Java microgenerator ([#859](https://www.github.com/googleapis/java-spanner/issues/859)) ([7cdfb82](https://www.github.com/googleapis/java-spanner/commit/7cdfb82b40487600547d0bad92119508161ca689)) +* Point In Time Recovery (PITR) ([#452](https://www.github.com/googleapis/java-spanner/issues/452)) ([ab14a5e](https://www.github.com/googleapis/java-spanner/commit/ab14a5ec2dc2b7e2141305b5326f436eb6eee76f)) + + +### Bug Fixes + +* allows user-agent header with header provider ([#871](https://www.github.com/googleapis/java-spanner/issues/871)) ([3de7e2a](https://www.github.com/googleapis/java-spanner/commit/3de7e2a91349cac5d79a32d2cda7ca727140f0bf)) +* make compiled statements immutable ([#843](https://www.github.com/googleapis/java-spanner/issues/843)) ([118d1b3](https://www.github.com/googleapis/java-spanner/commit/118d1b31f5f7771023766fd72a8229db80f1f5a2)) + + +### Dependencies + +* update dependency com.google.cloud:google-cloud-monitoring to v2.0.12 ([#854](https://www.github.com/googleapis/java-spanner/issues/854)) ([58cebd8](https://www.github.com/googleapis/java-spanner/commit/58cebd85a9d82bd1526b9eae98892181f1a022f1)) +* update dependency com.google.cloud:google-cloud-trace to v1.2.11 ([#825](https://www.github.com/googleapis/java-spanner/issues/825)) ([49c8c5d](https://www.github.com/googleapis/java-spanner/commit/49c8c5d241803565fa9ff96ba55f3eb00ed5b85e)) + + +### Documentation + +* libraries-bom 16.4.0 ([#867](https://www.github.com/googleapis/java-spanner/issues/867)) ([5af3673](https://www.github.com/googleapis/java-spanner/commit/5af36739532037360dfd504a4a0988562550526c)) + ### [3.3.2](https://www.github.com/googleapis/java-spanner/compare/v3.3.1...v3.3.2) (2021-01-18) diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index 8abee2e6d37..14a3963a578 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.3.3-SNAPSHOT + 4.0.0 pom com.google.cloud @@ -64,43 +64,43 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 3.3.3-SNAPSHOT + 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 3.3.3-SNAPSHOT + 4.0.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 3.3.3-SNAPSHOT + 4.0.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 3.3.3-SNAPSHOT + 4.0.0 com.google.cloud google-cloud-spanner - 3.3.3-SNAPSHOT + 4.0.0 com.google.cloud google-cloud-spanner test-jar - 3.3.3-SNAPSHOT + 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 3.3.3-SNAPSHOT + 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 3.3.3-SNAPSHOT + 4.0.0 diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index 0ca5a2b953b..323dc2a364a 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.3.3-SNAPSHOT + 4.0.0 jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 3.3.3-SNAPSHOT + 4.0.0 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 869511d6101..5ee7143baf5 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.3.3-SNAPSHOT + 4.0.0 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.3.3-SNAPSHOT + 4.0.0 diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml index f58baec738d..1ba5438a566 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.3.3-SNAPSHOT + 4.0.0 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.3.3-SNAPSHOT + 4.0.0 diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml index 34324a8acd2..15ec20312d7 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.3.3-SNAPSHOT + 4.0.0 grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 3.3.3-SNAPSHOT + 4.0.0 diff --git a/pom.xml b/pom.xml index 7a1418ae958..55f71d6c3cf 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-spanner-parent pom - 3.3.3-SNAPSHOT + 4.0.0 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.3.3-SNAPSHOT + 4.0.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 3.3.3-SNAPSHOT + 4.0.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 3.3.3-SNAPSHOT + 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 3.3.3-SNAPSHOT + 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 3.3.3-SNAPSHOT + 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 3.3.3-SNAPSHOT + 4.0.0 com.google.cloud google-cloud-spanner - 3.3.3-SNAPSHOT + 4.0.0 diff --git a/proto-google-cloud-spanner-admin-database-v1/pom.xml b/proto-google-cloud-spanner-admin-database-v1/pom.xml index 8b046befb56..a67c464ecd0 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.3.3-SNAPSHOT + 4.0.0 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.3.3-SNAPSHOT + 4.0.0 diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml index fd769578fe2..5de65e2e2a6 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.3.3-SNAPSHOT + 4.0.0 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.3.3-SNAPSHOT + 4.0.0 diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml index b30399bf7cf..d1251b3ff1b 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.3.3-SNAPSHOT + 4.0.0 proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 3.3.3-SNAPSHOT + 4.0.0 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 5bbf704d5f1..b3006bd7483 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -31,7 +31,7 @@ com.google.cloud google-cloud-spanner - 3.3.3-SNAPSHOT + 4.0.0 diff --git a/versions.txt b/versions.txt index e458c81d7ac..d07c027940c 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.3.2:3.3.3-SNAPSHOT -proto-google-cloud-spanner-v1:3.3.2:3.3.3-SNAPSHOT -proto-google-cloud-spanner-admin-database-v1:3.3.2:3.3.3-SNAPSHOT -grpc-google-cloud-spanner-v1:3.3.2:3.3.3-SNAPSHOT -grpc-google-cloud-spanner-admin-instance-v1:3.3.2:3.3.3-SNAPSHOT -grpc-google-cloud-spanner-admin-database-v1:3.3.2:3.3.3-SNAPSHOT -google-cloud-spanner:3.3.2:3.3.3-SNAPSHOT \ No newline at end of file +proto-google-cloud-spanner-admin-instance-v1:4.0.0:4.0.0 +proto-google-cloud-spanner-v1:4.0.0:4.0.0 +proto-google-cloud-spanner-admin-database-v1:4.0.0:4.0.0 +grpc-google-cloud-spanner-v1:4.0.0:4.0.0 +grpc-google-cloud-spanner-admin-instance-v1:4.0.0:4.0.0 +grpc-google-cloud-spanner-admin-database-v1:4.0.0:4.0.0 +google-cloud-spanner:4.0.0:4.0.0 \ No newline at end of file