diff --git a/.kokoro/continuous/dependencies.cfg b/.kokoro/continuous/dependencies.cfg deleted file mode 100644 index 808bf138e1c..00000000000 --- a/.kokoro/continuous/dependencies.cfg +++ /dev/null @@ -1,12 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java8" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/java-spanner/.kokoro/dependencies.sh" -} diff --git a/.kokoro/continuous/integration.cfg b/.kokoro/continuous/integration.cfg deleted file mode 100644 index 3b017fc80f0..00000000000 --- a/.kokoro/continuous/integration.cfg +++ /dev/null @@ -1,7 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java8" -} diff --git a/.kokoro/continuous/java11-samples.cfg b/.kokoro/continuous/java11-samples.cfg deleted file mode 100644 index 8f8fd6fbfaa..00000000000 --- a/.kokoro/continuous/java11-samples.cfg +++ /dev/null @@ -1,32 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java11" -} - -env_vars: { - key: "JOB_TYPE" - value: "samples" -} - -env_vars: { - key: "GCLOUD_PROJECT" - value: "gcloud-devel" -} - -env_vars: { - key: "GOOGLE_APPLICATION_CREDENTIALS" - value: "keystore/73713_java_it_service_account" -} - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "java_it_service_account" - } - } -} - diff --git a/.kokoro/continuous/java11.cfg b/.kokoro/continuous/java11.cfg deleted file mode 100644 index 709f2b4c73d..00000000000 --- a/.kokoro/continuous/java11.cfg +++ /dev/null @@ -1,7 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java11" -} diff --git a/.kokoro/continuous/java7.cfg b/.kokoro/continuous/java7.cfg deleted file mode 100644 index cb24f44eea3..00000000000 --- a/.kokoro/continuous/java7.cfg +++ /dev/null @@ -1,7 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java7" -} diff --git a/.kokoro/continuous/java8-osx.cfg b/.kokoro/continuous/java8-osx.cfg deleted file mode 100644 index 63f547222f5..00000000000 --- a/.kokoro/continuous/java8-osx.cfg +++ /dev/null @@ -1,3 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -build_file: "java-spanner/.kokoro/build.sh" diff --git a/.kokoro/continuous/java8-samples.cfg b/.kokoro/continuous/java8-samples.cfg deleted file mode 100644 index ecbc525719e..00000000000 --- a/.kokoro/continuous/java8-samples.cfg +++ /dev/null @@ -1,32 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java8" -} - -env_vars: { - key: "JOB_TYPE" - value: "samples" -} - -env_vars: { - key: "GCLOUD_PROJECT" - value: "gcloud-devel" -} - -env_vars: { - key: "GOOGLE_APPLICATION_CREDENTIALS" - value: "keystore/73713_java_it_service_account" -} - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "java_it_service_account" - } - } -} - diff --git a/.kokoro/continuous/java8-win.cfg b/.kokoro/continuous/java8-win.cfg deleted file mode 100644 index b219b38ad4a..00000000000 --- a/.kokoro/continuous/java8-win.cfg +++ /dev/null @@ -1,3 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -build_file: "java-spanner/.kokoro/build.bat" diff --git a/.kokoro/continuous/lint.cfg b/.kokoro/continuous/lint.cfg deleted file mode 100644 index 6d323c8ae76..00000000000 --- a/.kokoro/continuous/lint.cfg +++ /dev/null @@ -1,13 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. - -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java8" -} - -env_vars: { - key: "JOB_TYPE" - value: "lint" -} \ No newline at end of file diff --git a/.kokoro/continuous/propose_release.cfg b/.kokoro/continuous/propose_release.cfg deleted file mode 100644 index 7fa8a9708e3..00000000000 --- a/.kokoro/continuous/propose_release.cfg +++ /dev/null @@ -1,53 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "java-spanner/.kokoro/trampoline.sh" - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/node:10-user" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/java-spanner/.kokoro/continuous/propose_release.sh" -} - -# tokens used by release-please to keep an up-to-date release PR. -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "github-magic-proxy-key-release-please" - } - } -} - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "github-magic-proxy-token-release-please" - } - } -} - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "github-magic-proxy-url-release-please" - } - } -} diff --git a/.kokoro/continuous/propose_release.sh b/.kokoro/continuous/propose_release.sh deleted file mode 100755 index 3a4d950e968..00000000000 --- a/.kokoro/continuous/propose_release.sh +++ /dev/null @@ -1,29 +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 -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -eo pipefail - -export NPM_CONFIG_PREFIX=/home/node/.npm-global - -if [ -f ${KOKORO_KEYSTORE_DIR}/73713_github-magic-proxy-url-release-please ]; then - # Groom the release PR as new commits are merged. - npx release-please release-pr --token=${KOKORO_KEYSTORE_DIR}/73713_github-magic-proxy-token-release-please \ - --repo-url=googleapis/java-spanner \ - --package-name="spanner" \ - --api-url=${KOKORO_KEYSTORE_DIR}/73713_github-magic-proxy-url-release-please \ - --proxy-key=${KOKORO_KEYSTORE_DIR}/73713_github-magic-proxy-key-release-please \ - --release-type=java-yoshi -fi diff --git a/.kokoro/continuous/samples.cfg b/.kokoro/continuous/samples.cfg deleted file mode 100644 index fa7b493d0b5..00000000000 --- a/.kokoro/continuous/samples.cfg +++ /dev/null @@ -1,31 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java8" -} - -env_vars: { - key: "JOB_TYPE" - value: "samples" -} - -env_vars: { - key: "GCLOUD_PROJECT" - value: "gcloud-devel" -} - -env_vars: { - key: "GOOGLE_APPLICATION_CREDENTIALS" - value: "keystore/73713_java_it_service_account" -} - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "java_it_service_account" - } - } -} diff --git a/.kokoro/dependencies.sh b/.kokoro/dependencies.sh index cee4f11e754..c91e5a56937 100755 --- a/.kokoro/dependencies.sh +++ b/.kokoro/dependencies.sh @@ -43,12 +43,13 @@ function completenessCheck() { # Output dep list with compile scope generated using the original pom # Running mvn dependency:list on Java versions that support modules will also include the module of the dependency. # This is stripped from the output as it is not present in the flattened pom. + # Only dependencies with 'compile' or 'runtime' scope are included from original dependency list. msg "Generating dependency list using original pom..." - mvn dependency:list -f pom.xml -Dsort=true | grep '\[INFO] .*:.*:.*:.*:.*' | sed -e s/\\s--\\smodule.*// | grep -v ':test$' >.org-list.txt + mvn dependency:list -f pom.xml -DincludeScope=runtime -Dsort=true | grep '\[INFO] .*:.*:.*:.*:.*' | sed -e s/\\s--\\smodule.*// >.org-list.txt - # Output dep list generated using the flattened pom (test scope deps are ommitted) + # Output dep list generated using the flattened pom (only 'compile' and 'runtime' scopes) msg "Generating dependency list using flattened pom..." - mvn dependency:list -f .flattened-pom.xml -Dsort=true | grep '\[INFO] .*:.*:.*:.*:.*' >.new-list.txt + mvn dependency:list -f .flattened-pom.xml -DincludeScope=runtime -Dsort=true | grep '\[INFO] .*:.*:.*:.*:.*' >.new-list.txt # Compare two dependency lists msg "Comparing dependency lists..." @@ -85,4 +86,4 @@ then else msg "Errors found. See log statements above." exit 1 -fi +fi \ No newline at end of file diff --git a/.kokoro/nightly/dependencies.cfg b/.kokoro/nightly/dependencies.cfg deleted file mode 100644 index 808bf138e1c..00000000000 --- a/.kokoro/nightly/dependencies.cfg +++ /dev/null @@ -1,12 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java8" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/java-spanner/.kokoro/dependencies.sh" -} diff --git a/.kokoro/nightly/lint.cfg b/.kokoro/nightly/lint.cfg deleted file mode 100644 index 6d323c8ae76..00000000000 --- a/.kokoro/nightly/lint.cfg +++ /dev/null @@ -1,13 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. - -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java8" -} - -env_vars: { - key: "JOB_TYPE" - value: "lint" -} \ No newline at end of file diff --git a/.kokoro/release/publish_javadoc.cfg b/.kokoro/release/publish_javadoc.cfg index 2b07d18e99e..16043d07dbe 100644 --- a/.kokoro/release/publish_javadoc.cfg +++ b/.kokoro/release/publish_javadoc.cfg @@ -1,14 +1,24 @@ # Format: //devtools/kokoro/config/proto/build.proto + +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/doc-templates/" + env_vars: { key: "STAGING_BUCKET" value: "docs-staging" } +env_vars: { + key: "STAGING_BUCKET_V2" + value: "docs-staging-v2-staging" + # Production will be at: docs-staging-v2 +} + env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/java-spanner/.kokoro/release/publish_javadoc.sh" } + before_action { fetch_keystore { keystore_resource { diff --git a/.kokoro/release/publish_javadoc.sh b/.kokoro/release/publish_javadoc.sh index 2809dfdfb8d..fbd48654eeb 100755 --- a/.kokoro/release/publish_javadoc.sh +++ b/.kokoro/release/publish_javadoc.sh @@ -24,6 +24,11 @@ if [[ -z "${STAGING_BUCKET}" ]]; then exit 1 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")/../../ @@ -31,13 +36,13 @@ pushd $(dirname "$0")/../../ python3 -m pip install gcp-docuploader # compile all packages -mvn clean install -B -DskipTests=true +mvn clean install -B -q -DskipTests=true NAME=google-cloud-spanner VERSION=$(grep ${NAME}: versions.txt | cut -d: -f3) # build the docs -mvn site -B +mvn site -B -q pushd target/site/apidocs @@ -53,3 +58,19 @@ python3 -m docuploader upload . \ --staging-bucket ${STAGING_BUCKET} popd + +# V2 +mvn clean site -B -q -Ddevsite.template="${KOKORO_GFILE_DIR}/java/" + +pushd target/devsite + +# create metadata +python3 -m docuploader create-metadata \ + --name ${NAME} \ + --version ${VERSION} \ + --language java + +# upload docs +python3 -m docuploader upload . \ + --credentials ${CREDENTIALS} \ + --staging-bucket ${STAGING_BUCKET_V2} diff --git a/CHANGELOG.md b/CHANGELOG.md index 689841d4243..fdc9ffa2bef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## [1.60.0](https://www.github.com/googleapis/java-spanner/compare/v1.59.0...v1.60.0) (2020-08-18) + + +### Features + +* adds clirr check on pre-commit hook ([#388](https://www.github.com/googleapis/java-spanner/issues/388)) ([bd5c93f](https://www.github.com/googleapis/java-spanner/commit/bd5c93f045e06372b2235f3d350bade93bff2c24)) +* include SQL statement in error message ([#355](https://www.github.com/googleapis/java-spanner/issues/355)) ([cc5ac48](https://www.github.com/googleapis/java-spanner/commit/cc5ac48232b6e4550b98d213c5877d6ec37b293f)) + + +### Bug Fixes + +* enables emulator tests ([#380](https://www.github.com/googleapis/java-spanner/issues/380)) ([f61c6d0](https://www.github.com/googleapis/java-spanner/commit/f61c6d0d332f15826499996a292acc7cbab267a7)) +* remove custom timeout and retry settings ([#365](https://www.github.com/googleapis/java-spanner/issues/365)) ([f6afd21](https://www.github.com/googleapis/java-spanner/commit/f6afd213430d3f06d9a72c64a5c37172840fed0e)) +* remove unused kokoro files ([#367](https://www.github.com/googleapis/java-spanner/issues/367)) ([6125c7d](https://www.github.com/googleapis/java-spanner/commit/6125c7d221c77f4c42497b72107627ee09312813)) +* retry pdml transaction on EOS internal error ([#360](https://www.github.com/googleapis/java-spanner/issues/360)) ([a53d736](https://www.github.com/googleapis/java-spanner/commit/a53d7369bb2a8640ab42e409632b352decbdbf5e)) +* sets the project for the integration tests ([#386](https://www.github.com/googleapis/java-spanner/issues/386)) ([c8fa458](https://www.github.com/googleapis/java-spanner/commit/c8fa458f5369a09c780ee38ecc09bd2562e8f987)) + + +### Dependencies + +* stop auto updates of commons-lang3 ([#362](https://www.github.com/googleapis/java-spanner/issues/362)) ([8f07ed6](https://www.github.com/googleapis/java-spanner/commit/8f07ed6b44f9c70f56b9ee2e4505c40385337ca7)) +* update dependency com.google.cloud:google-cloud-shared-dependencies to v0.8.6 ([#374](https://www.github.com/googleapis/java-spanner/issues/374)) ([6f47b8a](https://www.github.com/googleapis/java-spanner/commit/6f47b8a759643f772230df0c2e153338d44f70ce)) +* update dependency org.openjdk.jmh:jmh-core to v1.24 ([#375](https://www.github.com/googleapis/java-spanner/issues/375)) ([94f568c](https://www.github.com/googleapis/java-spanner/commit/94f568cf731ba22cac7f0d898d7776a3cc2c178f)) +* update dependency org.openjdk.jmh:jmh-core to v1.25 ([#382](https://www.github.com/googleapis/java-spanner/issues/382)) ([ec7888e](https://www.github.com/googleapis/java-spanner/commit/ec7888e1d62cf800bf6ad166d242e89443ddc7aa)) +* update dependency org.openjdk.jmh:jmh-generator-annprocess to v1.25 ([#376](https://www.github.com/googleapis/java-spanner/issues/376)) ([8ffdc48](https://www.github.com/googleapis/java-spanner/commit/8ffdc481e15901f78eac592bd8d4bef33ac3378a)) + ## [1.59.0](https://www.github.com/googleapis/java-spanner/compare/v1.58.0...v1.59.0) (2020-07-16) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f2dbdee06bc..53d24601789 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,7 +48,11 @@ integration tests. ```bash export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account.json -mvn -Penable-integration-tests clean verify +mvn \ + -Penable-integration-tests \ + -Dspanner.testenv.instance=projects//instances/ \ + -Dspanner.gce.config.project_id= \ + clean verify ``` ## Code Samples diff --git a/README.md b/README.md index aa8727ea64f..7d8c5b48612 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 - 8.1.0 + 9.0.0 pom import @@ -38,7 +38,7 @@ If you are using Maven without BOM, add this to your dependencies: com.google.cloud google-cloud-spanner - 1.58.0 + 1.59.0 ``` @@ -47,11 +47,11 @@ If you are using Maven without BOM, add this to your dependencies: If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-spanner:1.59.0' +compile 'com.google.cloud:google-cloud-spanner:1.60.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "1.59.0" +libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "1.60.0" ``` [//]: # ({x-version-update-end}) diff --git a/git-hooks/pre-commit b/git-hooks/pre-commit new file mode 100755 index 00000000000..70e559f9fbc --- /dev/null +++ b/git-hooks/pre-commit @@ -0,0 +1,11 @@ +#!/bin/sh +# +# Copy this file into your .git/hooks folder + +set -e + +# Checks for: +# - Formatting errors +# - Clirr errors +mvn com.coveo:fmt-maven-plugin:check clirr:check + diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index 35e16ef000b..969f9630dba 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 - 1.59.0 + 1.60.0 pom com.google.cloud @@ -64,43 +64,43 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 1.59.0 + 1.60.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 1.59.0 + 1.60.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 1.59.0 + 1.60.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 1.59.0 + 1.60.0 com.google.cloud google-cloud-spanner - 1.59.0 + 1.60.0 com.google.cloud google-cloud-spanner test-jar - 1.59.0 + 1.60.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 1.59.0 + 1.60.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 1.59.0 + 1.60.0 diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index 1d15f103e30..3f7a8f3ce27 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 - 1.59.0 + 1.60.0 jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 1.59.0 + 1.60.0 google-cloud-spanner @@ -69,6 +69,7 @@ com.google.cloud.spanner.GceTestEnvConfig projects/gcloud-devel/instances/spanner-testing + gcloud-devel 3000 @@ -305,13 +306,13 @@ org.openjdk.jmh jmh-core - 1.23 + 1.25 test org.openjdk.jmh jmh-generator-annprocess - 1.23 + 1.25 test @@ -363,4 +364,4 @@ - \ No newline at end of file + diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java index bc4a8685648..f05fd10dda3 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java @@ -607,7 +607,7 @@ ExecuteBatchDmlRequest.Builder getExecuteBatchDmlRequestBuilder(Iterable startStream(@Nullable ByteString resumeToken) { - GrpcStreamIterator stream = new GrpcStreamIterator(prefetchChunks); + GrpcStreamIterator stream = new GrpcStreamIterator(statement, prefetchChunks); if (resumeToken != null) { request.setResumeToken(resumeToken); } 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 11f36f4a92b..43bef07ce7a 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 @@ -763,16 +763,24 @@ interface CloseableIterator extends Iterator { @VisibleForTesting static class GrpcStreamIterator extends AbstractIterator implements CloseableIterator { + private static final Logger logger = Logger.getLogger(GrpcStreamIterator.class.getName()); private static final PartialResultSet END_OF_STREAM = PartialResultSet.newBuilder().build(); private final ConsumerImpl consumer = new ConsumerImpl(); private final BlockingQueue stream; + private final Statement statement; private SpannerRpc.StreamingCall call; private SpannerException error; - // Visible for testing. + @VisibleForTesting GrpcStreamIterator(int prefetchChunks) { + this(null, prefetchChunks); + } + + @VisibleForTesting + GrpcStreamIterator(Statement statement, int prefetchChunks) { + this.statement = statement; // One extra to allow for END_OF_STREAM message. this.stream = new LinkedBlockingQueue<>(prefetchChunks + 1); } @@ -839,6 +847,23 @@ public void onCompleted() { @Override public void onError(SpannerException e) { + if (statement != null) { + if (logger.isLoggable(Level.FINEST)) { + // Include parameter values if logging level is set to FINEST or higher. + e = + SpannerExceptionFactory.newSpannerExceptionPreformatted( + e.getErrorCode(), + String.format("%s - Statement: '%s'", e.getMessage(), statement.toString()), + e); + logger.log(Level.FINEST, "Error executing statement", e); + } else { + e = + SpannerExceptionFactory.newSpannerExceptionPreformatted( + e.getErrorCode(), + String.format("%s - Statement: '%s'", e.getMessage(), statement.getSql()), + e); + } + } error = e; addToStream(END_OF_STREAM); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/IsRetryableInternalError.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/IsRetryableInternalError.java new file mode 100644 index 00000000000..c62b4a2e06e --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/IsRetryableInternalError.java @@ -0,0 +1,51 @@ +/* + * 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; + +import com.google.api.gax.rpc.InternalException; +import com.google.common.base.Predicate; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; + +public class IsRetryableInternalError implements Predicate { + + private static final String HTTP2_ERROR_MESSAGE = "HTTP/2 error code: INTERNAL_ERROR"; + private static final String CONNECTION_CLOSED_ERROR_MESSAGE = + "Connection closed with unknown cause"; + private static final String EOS_ERROR_MESSAGE = + "Received unexpected EOS on DATA frame from server"; + + @Override + public boolean apply(Throwable cause) { + if (isInternalError(cause)) { + if (cause.getMessage().contains(HTTP2_ERROR_MESSAGE)) { + return true; + } else if (cause.getMessage().contains(CONNECTION_CLOSED_ERROR_MESSAGE)) { + return true; + } else if (cause.getMessage().contains(EOS_ERROR_MESSAGE)) { + return true; + } + } + return false; + } + + private boolean isInternalError(Throwable cause) { + return (cause instanceof InternalException) + || (cause instanceof StatusRuntimeException + && ((StatusRuntimeException) cause).getStatus().getCode() == Status.Code.INTERNAL); + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/IsSslHandshakeException.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/IsSslHandshakeException.java new file mode 100644 index 00000000000..53ff151a83c --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/IsSslHandshakeException.java @@ -0,0 +1,28 @@ +/* + * 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; + +import com.google.common.base.Predicate; +import javax.net.ssl.SSLHandshakeException; + +public class IsSslHandshakeException implements Predicate { + + @Override + public boolean apply(Throwable input) { + return input instanceof SSLHandshakeException; + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/PartitionedDMLTransaction.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/PartitionedDMLTransaction.java deleted file mode 100644 index a938d174476..00000000000 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/PartitionedDMLTransaction.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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. - */ - -package com.google.cloud.spanner; - -import static com.google.common.base.Preconditions.checkState; - -import com.google.api.gax.grpc.GrpcStatusCode; -import com.google.api.gax.rpc.DeadlineExceededException; -import com.google.api.gax.rpc.ServerStream; -import com.google.api.gax.rpc.UnavailableException; -import com.google.cloud.spanner.SessionImpl.SessionTransaction; -import com.google.cloud.spanner.spi.v1.SpannerRpc; -import com.google.common.base.Stopwatch; -import com.google.protobuf.ByteString; -import com.google.spanner.v1.BeginTransactionRequest; -import com.google.spanner.v1.ExecuteSqlRequest; -import com.google.spanner.v1.ExecuteSqlRequest.QueryMode; -import com.google.spanner.v1.PartialResultSet; -import com.google.spanner.v1.Transaction; -import com.google.spanner.v1.TransactionOptions; -import com.google.spanner.v1.TransactionSelector; -import io.grpc.Status.Code; -import io.opencensus.trace.Span; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.threeten.bp.Duration; -import org.threeten.bp.temporal.ChronoUnit; - -/** Partitioned DML transaction for bulk updates and deletes. */ -class PartitionedDMLTransaction implements SessionTransaction { - private static final Logger log = Logger.getLogger(PartitionedDMLTransaction.class.getName()); - - private final SessionImpl session; - private final SpannerRpc rpc; - private volatile boolean isValid = true; - - PartitionedDMLTransaction(SessionImpl session, SpannerRpc rpc) { - this.session = session; - this.rpc = rpc; - } - - private ByteString initTransaction() { - final BeginTransactionRequest request = - BeginTransactionRequest.newBuilder() - .setSession(session.getName()) - .setOptions( - TransactionOptions.newBuilder() - .setPartitionedDml(TransactionOptions.PartitionedDml.getDefaultInstance())) - .build(); - Transaction txn = rpc.beginTransaction(request, session.getOptions()); - if (txn.getId().isEmpty()) { - throw SpannerExceptionFactory.newSpannerException( - ErrorCode.INTERNAL, - "Failed to init transaction, missing transaction id\n" + session.getName()); - } - return txn.getId(); - } - - /** - * Executes the {@link Statement} using a partitioned dml transaction with automatic retry if the - * transaction was aborted. The update method uses the ExecuteStreamingSql RPC to execute the - * statement, and will retry the stream if an {@link UnavailableException} is thrown, using the - * last seen resume token if the server returns any. - */ - long executeStreamingPartitionedUpdate(final Statement statement, final Duration timeout) { - checkState(isValid, "Partitioned DML has been invalidated by a new operation on the session"); - log.log(Level.FINER, "Starting PartitionedUpdate statement"); - boolean foundStats = false; - long updateCount = 0L; - Stopwatch stopWatch = createStopwatchStarted(); - try { - // Loop to catch AbortedExceptions. - while (true) { - ByteString resumeToken = ByteString.EMPTY; - try { - ByteString transactionId = initTransaction(); - final ExecuteSqlRequest.Builder builder = - ExecuteSqlRequest.newBuilder() - .setSql(statement.getSql()) - .setQueryMode(QueryMode.NORMAL) - .setSession(session.getName()) - .setTransaction(TransactionSelector.newBuilder().setId(transactionId).build()); - Map stmtParameters = statement.getParameters(); - if (!stmtParameters.isEmpty()) { - com.google.protobuf.Struct.Builder paramsBuilder = builder.getParamsBuilder(); - for (Map.Entry param : stmtParameters.entrySet()) { - paramsBuilder.putFields(param.getKey(), param.getValue().toProto()); - builder.putParamTypes(param.getKey(), param.getValue().getType().toProto()); - } - } - while (true) { - Duration remainingTimeout = - timeout.minus(stopWatch.elapsed(TimeUnit.MILLISECONDS), ChronoUnit.MILLIS); - if (remainingTimeout.isNegative() || remainingTimeout.isZero()) { - // The total deadline has been exceeded while retrying. - throw new DeadlineExceededException( - null, GrpcStatusCode.of(Code.DEADLINE_EXCEEDED), false); - } - try { - builder.setResumeToken(resumeToken); - ServerStream stream = - rpc.executeStreamingPartitionedDml( - builder.build(), session.getOptions(), remainingTimeout); - for (PartialResultSet rs : stream) { - if (rs.getResumeToken() != null && !ByteString.EMPTY.equals(rs.getResumeToken())) { - resumeToken = rs.getResumeToken(); - } - if (rs.hasStats()) { - foundStats = true; - updateCount += rs.getStats().getRowCountLowerBound(); - } - } - break; - } catch (UnavailableException e) { - // Retry the stream in the same transaction if the stream breaks with - // UnavailableException and we have a resume token. Otherwise, we just retry the - // entire transaction. - if (!ByteString.EMPTY.equals(resumeToken)) { - log.log( - Level.FINER, - "Retrying PartitionedDml stream using resume token '" - + resumeToken.toStringUtf8() - + "' because of broken stream", - e); - } else { - throw new com.google.api.gax.rpc.AbortedException( - e, GrpcStatusCode.of(Code.ABORTED), true); - } - } - } - break; - } catch (com.google.api.gax.rpc.AbortedException e) { - // Retry using a new transaction but with the same session if the transaction is aborted. - log.log(Level.FINER, "Retrying PartitionedDml transaction after AbortedException", e); - } - } - if (!foundStats) { - throw SpannerExceptionFactory.newSpannerException( - ErrorCode.INVALID_ARGUMENT, - "Partitioned DML response missing stats possibly due to non-DML statement as input"); - } - log.log(Level.FINER, "Finished PartitionedUpdate statement"); - return updateCount; - } catch (Exception e) { - throw SpannerExceptionFactory.newSpannerException(e); - } - } - - Stopwatch createStopwatchStarted() { - return Stopwatch.createStarted(); - } - - @Override - public void invalidate() { - isValid = false; - } - - // No-op method needed to implement SessionTransaction interface. - @Override - public void setSpan(Span span) {} -} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/PartitionedDmlTransaction.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/PartitionedDmlTransaction.java new file mode 100644 index 00000000000..da92b6f9c64 --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/PartitionedDmlTransaction.java @@ -0,0 +1,208 @@ +/* + * 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; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.api.gax.grpc.GrpcStatusCode; +import com.google.api.gax.rpc.AbortedException; +import com.google.api.gax.rpc.DeadlineExceededException; +import com.google.api.gax.rpc.InternalException; +import com.google.api.gax.rpc.ServerStream; +import com.google.api.gax.rpc.UnavailableException; +import com.google.cloud.spanner.spi.v1.SpannerRpc; +import com.google.common.base.Stopwatch; +import com.google.common.base.Ticker; +import com.google.protobuf.ByteString; +import com.google.spanner.v1.BeginTransactionRequest; +import com.google.spanner.v1.ExecuteSqlRequest; +import com.google.spanner.v1.PartialResultSet; +import com.google.spanner.v1.Transaction; +import com.google.spanner.v1.TransactionOptions; +import com.google.spanner.v1.TransactionSelector; +import io.grpc.Status; +import io.opencensus.trace.Span; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.threeten.bp.Duration; +import org.threeten.bp.temporal.ChronoUnit; + +public class PartitionedDmlTransaction implements SessionImpl.SessionTransaction { + + private static final Logger LOGGER = Logger.getLogger(PartitionedDmlTransaction.class.getName()); + + private final SessionImpl session; + private final SpannerRpc rpc; + private final Ticker ticker; + private final IsRetryableInternalError isRetryableInternalErrorPredicate; + private volatile boolean isValid = true; + + PartitionedDmlTransaction(SessionImpl session, SpannerRpc rpc, Ticker ticker) { + this.session = session; + this.rpc = rpc; + this.ticker = ticker; + this.isRetryableInternalErrorPredicate = new IsRetryableInternalError(); + } + + /** + * Executes the {@link Statement} using a partitioned dml transaction with automatic retry if the + * transaction was aborted. The update method uses the ExecuteStreamingSql RPC to execute the + * statement, and will retry the stream if an {@link UnavailableException} is thrown, using the + * last seen resume token if the server returns any. + */ + long executeStreamingPartitionedUpdate(final Statement statement, final Duration timeout) { + checkState(isValid, "Partitioned DML has been invalidated by a new operation on the session"); + LOGGER.log(Level.FINER, "Starting PartitionedUpdate statement"); + + ByteString resumeToken = ByteString.EMPTY; + boolean foundStats = false; + long updateCount = 0L; + Stopwatch stopwatch = Stopwatch.createStarted(ticker); + + try { + ExecuteSqlRequest request = newTransactionRequestFrom(statement); + + while (true) { + final Duration remainingTimeout = tryUpdateTimeout(timeout, stopwatch); + + try { + ServerStream stream = + rpc.executeStreamingPartitionedDml(request, session.getOptions(), remainingTimeout); + + for (PartialResultSet rs : stream) { + if (rs.getResumeToken() != null && !rs.getResumeToken().isEmpty()) { + resumeToken = rs.getResumeToken(); + } + if (rs.hasStats()) { + foundStats = true; + updateCount += rs.getStats().getRowCountLowerBound(); + } + } + break; + } catch (UnavailableException e) { + LOGGER.log( + Level.FINER, "Retrying PartitionedDml transaction after UnavailableException", e); + request = resumeOrRestartRequest(resumeToken, statement, request); + } catch (InternalException e) { + if (!isRetryableInternalErrorPredicate.apply(e)) { + throw e; + } + + LOGGER.log( + Level.FINER, "Retrying PartitionedDml transaction after InternalException - EOS", e); + request = resumeOrRestartRequest(resumeToken, statement, request); + } catch (AbortedException e) { + LOGGER.log(Level.FINER, "Retrying PartitionedDml transaction after AbortedException", e); + resumeToken = ByteString.EMPTY; + foundStats = false; + updateCount = 0L; + request = newTransactionRequestFrom(statement); + } + } + if (!foundStats) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.INVALID_ARGUMENT, + "Partitioned DML response missing stats possibly due to non-DML statement as input"); + } + LOGGER.log(Level.FINER, "Finished PartitionedUpdate statement"); + return updateCount; + } catch (Exception e) { + throw SpannerExceptionFactory.newSpannerException(e); + } + } + + @Override + public void invalidate() { + isValid = false; + } + + // No-op method needed to implement SessionTransaction interface. + @Override + public void setSpan(Span span) {} + + private Duration tryUpdateTimeout(final Duration timeout, final Stopwatch stopwatch) { + final Duration remainingTimeout = + timeout.minus(stopwatch.elapsed(TimeUnit.MILLISECONDS), ChronoUnit.MILLIS); + if (remainingTimeout.isNegative() || remainingTimeout.isZero()) { + // The total deadline has been exceeded while retrying. + throw new DeadlineExceededException( + null, GrpcStatusCode.of(Status.Code.DEADLINE_EXCEEDED), false); + } + return remainingTimeout; + } + + private ExecuteSqlRequest resumeOrRestartRequest( + final ByteString resumeToken, + final Statement statement, + final ExecuteSqlRequest originalRequest) { + if (resumeToken.isEmpty()) { + return newTransactionRequestFrom(statement); + } else { + return ExecuteSqlRequest.newBuilder(originalRequest).setResumeToken(resumeToken).build(); + } + } + + private ExecuteSqlRequest newTransactionRequestFrom(final Statement statement) { + ByteString transactionId = initTransaction(); + + final TransactionSelector transactionSelector = + TransactionSelector.newBuilder().setId(transactionId).build(); + final ExecuteSqlRequest.Builder builder = + ExecuteSqlRequest.newBuilder() + .setSql(statement.getSql()) + .setQueryMode(ExecuteSqlRequest.QueryMode.NORMAL) + .setSession(session.getName()) + .setTransaction(transactionSelector); + + setParameters(builder, statement.getParameters()); + + builder.setResumeToken(ByteString.EMPTY); + + return builder.build(); + } + + private ByteString initTransaction() { + final BeginTransactionRequest request = + BeginTransactionRequest.newBuilder() + .setSession(session.getName()) + .setOptions( + TransactionOptions.newBuilder() + .setPartitionedDml(TransactionOptions.PartitionedDml.getDefaultInstance())) + .build(); + Transaction tx = rpc.beginTransaction(request, session.getOptions()); + if (tx.getId().isEmpty()) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.INTERNAL, + "Failed to init transaction, missing transaction id\n" + session.getName()); + } + return tx.getId(); + } + + private void setParameters( + final ExecuteSqlRequest.Builder requestBuilder, + final Map statementParameters) { + if (!statementParameters.isEmpty()) { + com.google.protobuf.Struct.Builder paramsBuilder = requestBuilder.getParamsBuilder(); + for (Map.Entry param : statementParameters.entrySet()) { + paramsBuilder.putFields(param.getKey(), param.getValue().toProto()); + requestBuilder.putParamTypes(param.getKey(), param.getValue().getType().toProto()); + } + } + } +} 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 ce4d27e94ea..6a91d85fef4 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 @@ -28,6 +28,7 @@ import com.google.cloud.spanner.SessionClient.SessionId; import com.google.cloud.spanner.TransactionRunnerImpl.TransactionContextImpl; import com.google.cloud.spanner.spi.v1.SpannerRpc; +import com.google.common.base.Ticker; import com.google.common.collect.Lists; import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.ByteString; @@ -114,7 +115,8 @@ void setCurrentSpan(Span span) { @Override public long executePartitionedUpdate(Statement stmt) { setActive(null); - PartitionedDMLTransaction txn = new PartitionedDMLTransaction(this, spanner.getRpc()); + PartitionedDmlTransaction txn = + new PartitionedDmlTransaction(this, spanner.getRpc(), Ticker.systemTicker()); return txn.executeStreamingPartitionedUpdate( stmt, spanner.getOptions().getPartitionedDmlTimeout()); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java index a86d5463c9f..3fa756875b9 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java @@ -26,12 +26,10 @@ import io.grpc.Context; import io.grpc.Metadata; import io.grpc.Status; -import io.grpc.StatusRuntimeException; import io.grpc.protobuf.ProtoUtils; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeoutException; import javax.annotation.Nullable; -import javax.net.ssl.SSLHandshakeException; /** * A factory for creating instances of {@link SpannerException} and its subtypes. All creation of @@ -40,6 +38,7 @@ * ErrorCode#ABORTED} are always represented by {@link AbortedException}. */ public final class SpannerExceptionFactory { + static final String SESSION_RESOURCE_TYPE = "type.googleapis.com/google.spanner.v1.Session"; static final String DATABASE_RESOURCE_TYPE = "type.googleapis.com/google.spanner.admin.database.v1.Database"; @@ -188,7 +187,7 @@ private static ResourceInfo extractResourceInfo(Throwable cause) { return null; } - private static SpannerException newSpannerExceptionPreformatted( + static SpannerException newSpannerExceptionPreformatted( ErrorCode code, @Nullable String message, @Nullable Throwable cause) { // This is the one place in the codebase that is allowed to call constructors directly. DoNotConstructDirectly token = DoNotConstructDirectly.ALLOWED; @@ -257,35 +256,8 @@ private static boolean hasCauseMatching( } private static class Matchers { - static final Predicate isRetryableInternalError = - new Predicate() { - @Override - public boolean apply(Throwable cause) { - if (cause instanceof StatusRuntimeException - && ((StatusRuntimeException) cause).getStatus().getCode() == Status.Code.INTERNAL) { - if (cause.getMessage().contains("HTTP/2 error code: INTERNAL_ERROR")) { - // See b/25451313. - return true; - } - if (cause.getMessage().contains("Connection closed with unknown cause")) { - // See b/27794742. - return true; - } - if (cause - .getMessage() - .contains("Received unexpected EOS on DATA frame from server")) { - return true; - } - } - return false; - } - }; - static final Predicate isSSLHandshakeException = - new Predicate() { - @Override - public boolean apply(Throwable input) { - return input instanceof SSLHandshakeException; - } - }; + + static final Predicate isRetryableInternalError = new IsRetryableInternalError(); + static final Predicate isSSLHandshakeException = new IsSslHandshakeException(); } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java index 35a288530fa..bc3f513ce0d 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java @@ -19,12 +19,9 @@ import com.google.api.core.ApiFunction; import com.google.api.gax.core.ExecutorProvider; import com.google.api.gax.grpc.GrpcInterceptorProvider; -import com.google.api.gax.longrunning.OperationSnapshot; import com.google.api.gax.longrunning.OperationTimedPollAlgorithm; import com.google.api.gax.retrying.RetrySettings; -import com.google.api.gax.rpc.StatusCode; import com.google.api.gax.rpc.TransportChannelProvider; -import com.google.api.gax.rpc.UnaryCallSettings; import com.google.cloud.NoCredentials; import com.google.cloud.ServiceDefaults; import com.google.cloud.ServiceOptions; @@ -47,9 +44,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.google.spanner.admin.database.v1.CreateBackupRequest; -import com.google.spanner.admin.database.v1.CreateDatabaseRequest; -import com.google.spanner.admin.database.v1.RestoreDatabaseRequest; import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions; import io.grpc.CallCredentials; import io.grpc.CompressorRegistry; @@ -331,47 +325,15 @@ private Builder() { .setRpcTimeoutMultiplier(1.5) .setTotalTimeout(Duration.ofHours(48L)) .build()); - RetrySettings longRunningRetrySettings = - RetrySettings.newBuilder() - .setInitialRpcTimeout(Duration.ofSeconds(60L)) - .setMaxRpcTimeout(Duration.ofSeconds(600L)) - .setInitialRetryDelay(Duration.ofSeconds(20L)) - .setMaxRetryDelay(Duration.ofSeconds(45L)) - .setRetryDelayMultiplier(1.5) - .setRpcTimeoutMultiplier(1.5) - .setTotalTimeout(Duration.ofHours(48L)) - .build(); databaseAdminStubSettingsBuilder .createDatabaseOperationSettings() - .setPollingAlgorithm(longRunningPollingAlgorithm) - .setInitialCallSettings( - UnaryCallSettings - .newUnaryCallSettingsBuilder() - .setRetrySettings(longRunningRetrySettings) - .build()); + .setPollingAlgorithm(longRunningPollingAlgorithm); databaseAdminStubSettingsBuilder .createBackupOperationSettings() - .setPollingAlgorithm(longRunningPollingAlgorithm) - .setInitialCallSettings( - UnaryCallSettings - .newUnaryCallSettingsBuilder() - .setRetrySettings(longRunningRetrySettings) - .build()); + .setPollingAlgorithm(longRunningPollingAlgorithm); databaseAdminStubSettingsBuilder .restoreDatabaseOperationSettings() - .setPollingAlgorithm(longRunningPollingAlgorithm) - .setInitialCallSettings( - UnaryCallSettings - .newUnaryCallSettingsBuilder() - .setRetrySettings(longRunningRetrySettings) - .build()); - databaseAdminStubSettingsBuilder - .deleteBackupSettings() - .setRetrySettings(longRunningRetrySettings); - databaseAdminStubSettingsBuilder - .updateBackupSettings() - .setRetrySettings(longRunningRetrySettings) - .setRetryableCodes(StatusCode.Code.DEADLINE_EXCEEDED, StatusCode.Code.UNAVAILABLE); + .setPollingAlgorithm(longRunningPollingAlgorithm); } Builder(SpannerOptions options) { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/DatabaseAdminStubSettings.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/DatabaseAdminStubSettings.java index 7799b21fc3f..90882358b9a 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/DatabaseAdminStubSettings.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/DatabaseAdminStubSettings.java @@ -888,8 +888,8 @@ private static Builder initDefaults(Builder builder) { builder .updateBackupSettings() - .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("no_retry_1_codes")) - .setRetrySettings(RETRY_PARAM_DEFINITIONS.get("no_retry_1_params")); + .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("retry_policy_1_codes")) + .setRetrySettings(RETRY_PARAM_DEFINITIONS.get("retry_policy_1_params")); builder .deleteBackupSettings() diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/EmulatorSpannerHelper.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/EmulatorSpannerHelper.java new file mode 100644 index 00000000000..e7a4cb85225 --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/EmulatorSpannerHelper.java @@ -0,0 +1,35 @@ +/* + * 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.testing; + +import com.google.common.base.Strings; + +/** Utility class for checking emulator state for tests */ +public class EmulatorSpannerHelper { + + public static final String SPANNER_EMULATOR_HOST = "SPANNER_EMULATOR_HOST"; + + /** + * Checks whether the emulator is being used. This is done by checking if the + * SPANNER_EMULATOR_HOST environment variable is set. + * + * @return true if the emulator is being used. Returns false otherwise. + */ + public static boolean isUsingEmulator() { + return !Strings.isNullOrEmpty(System.getenv(SPANNER_EMULATOR_HOST)); + } +} 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 f20354950f0..e8a16d648a0 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 @@ -25,7 +25,6 @@ import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; -import com.google.common.base.Strings; import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; import java.util.ArrayList; import java.util.Arrays; @@ -58,8 +57,15 @@ public SpannerOptions getOptions() { return options; } + /** + * Checks whether the emulator is being used. + * + * @deprecated use {@link EmulatorSpannerHelper#isUsingEmulator()} instead. + * @return true if the emulator is being used. Returns false otherwise. + */ + @Deprecated public boolean isEmulator() { - return !Strings.isNullOrEmpty(System.getenv("SPANNER_EMULATOR_HOST")); + return EmulatorSpannerHelper.isUsingEmulator(); } public Spanner getClient() { 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 9bff8fbc25e..5e69b35cd3a 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 @@ -25,6 +25,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.spanner.AbstractResultSet.GrpcStreamIterator; import com.google.cloud.spanner.AsyncResultSet.CallbackResponse; import com.google.cloud.spanner.AsyncResultSet.ReadyCallback; import com.google.cloud.spanner.AsyncRunner.AsyncWork; @@ -33,6 +34,7 @@ import com.google.cloud.spanner.ReadContext.QueryAnalyzeMode; import com.google.cloud.spanner.TransactionRunner.TransactionCallable; import com.google.common.base.Stopwatch; +import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.SettableFuture; import com.google.protobuf.AbstractMessage; import com.google.spanner.v1.ExecuteSqlRequest; @@ -52,6 +54,8 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -1483,4 +1487,65 @@ public void testBatchCreateSessionsPermissionDenied() { } } } + + @Test + public void testExceptionIncludesStatement() { + mockSpanner.setExecuteStreamingSqlExecutionTime( + SimulatedExecutionTime.ofException( + Status.INVALID_ARGUMENT.withDescription("Invalid query").asRuntimeException())); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + try (ResultSet rs = + client + .singleUse() + .executeQuery( + Statement.newBuilder("SELECT * FROM FOO WHERE ID=@id").bind("id").to(1L).build())) { + rs.next(); + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + assertThat(e.getMessage()).contains("Statement: 'SELECT * FROM FOO WHERE ID=@id'"); + // The error message should normally not include the parameter values to prevent sensitive + // information from accidentally being logged. + assertThat(e.getMessage()).doesNotContain("id: 1"); + } + + mockSpanner.setExecuteStreamingSqlExecutionTime( + SimulatedExecutionTime.ofException( + Status.INVALID_ARGUMENT.withDescription("Invalid query").asRuntimeException())); + Logger logger = Logger.getLogger(GrpcStreamIterator.class.getName()); + Level currentLevel = logger.getLevel(); + try (ResultSet rs = + client + .singleUse() + .executeQuery( + Statement.newBuilder("SELECT * FROM FOO WHERE ID=@id").bind("id").to(1L).build())) { + logger.setLevel(Level.FINEST); + rs.next(); + fail("missing expected exception"); + } catch (SpannerException e) { + // With log level set to FINEST the error should also include the parameter values. + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + assertThat(e.getMessage()).contains("Statement: 'SELECT * FROM FOO WHERE ID=@id {id: 1}'"); + } finally { + logger.setLevel(currentLevel); + } + } + + @Test + public void testReadDoesNotIncludeStatement() { + mockSpanner.setStreamingReadExecutionTime( + SimulatedExecutionTime.ofException( + Status.INVALID_ARGUMENT.withDescription("Invalid read").asRuntimeException())); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + try (ResultSet rs = + client.singleUse().read("FOO", KeySet.singleKey(Key.of(1L)), ImmutableList.of("BAR"))) { + rs.next(); + fail("missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + assertThat(e.getMessage()).doesNotContain("Statement:"); + } + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolIntegrationTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITSessionPoolIntegrationTest.java similarity index 83% rename from google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolIntegrationTest.java rename to google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITSessionPoolIntegrationTest.java index b7ddb19223c..66256489e88 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolIntegrationTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITSessionPoolIntegrationTest.java @@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat; import com.google.cloud.grpc.GrpcTransportOptions.ExecutorFactory; +import com.google.cloud.spanner.SessionPool.PooledSessionFuture; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -41,7 +42,7 @@ */ @Category(IntegrationTest.class) @RunWith(JUnit4.class) -public class SessionPoolIntegrationTest { +public class ITSessionPoolIntegrationTest { @ClassRule public static IntegrationTestEnv env = new IntegrationTestEnv(); private static final String TABLE_NAME = "TestTable"; @@ -97,28 +98,27 @@ public ScheduledExecutorService get() { @Test public void sessionCreation() { - try (Session session = pool.getReadSession()) { - assertThat(session).isNotNull(); + try (PooledSessionFuture session = pool.getReadSession()) { + assertThat(session.get()).isNotNull(); } - try (Session session = pool.getReadSession()) { - assertThat(session).isNotNull(); - Session session2 = pool.getReadSession(); - assertThat(session2).isNotNull(); - session2.close(); + try (PooledSessionFuture session = pool.getReadSession(); + PooledSessionFuture session2 = pool.getReadSession()) { + assertThat(session.get()).isNotNull(); + assertThat(session2.get()).isNotNull(); } } @Test public void poolExhaustion() throws Exception { - Session session1 = pool.getReadSession(); - Session session2 = pool.getReadSession(); + Session session1 = pool.getReadSession().get(); + Session session2 = pool.getReadSession().get(); final CountDownLatch latch = new CountDownLatch(1); new Thread( new Runnable() { @Override public void run() { - try (Session session3 = pool.getReadSession()) { + try (Session session3 = pool.getReadSession().get()) { latch.countDown(); } } @@ -132,8 +132,8 @@ public void run() { @Test public void multipleWaiters() throws Exception { - Session session1 = pool.getReadSession(); - Session session2 = pool.getReadSession(); + Session session1 = pool.getReadSession().get(); + Session session2 = pool.getReadSession().get(); int numSessions = 5; final CountDownLatch latch = new CountDownLatch(numSessions); for (int i = 0; i < numSessions; i++) { @@ -141,7 +141,7 @@ public void multipleWaiters() throws Exception { new Runnable() { @Override public void run() { - try (Session session = pool.getReadSession()) { + try (Session session = pool.getReadSession().get()) { latch.countDown(); } } @@ -167,7 +167,9 @@ public void closeAfterInitialCreateDoesNotBlockIndefinitely() throws Exception { @Test public void closeWhenSessionsActiveFinishes() throws Exception { - Session session = pool.getReadSession(); + pool.getReadSession().get(); + // This will log a warning that a session has been leaked, as the session that we retrieved in + // the previous statement was never returned to the pool. pool.closeAsync(new SpannerImpl.ClosedException()).get(); } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IsRetryableInternalErrorTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IsRetryableInternalErrorTest.java new file mode 100644 index 00000000000..0f76da6692f --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IsRetryableInternalErrorTest.java @@ -0,0 +1,135 @@ +/* + * 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; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.api.gax.grpc.GrpcStatusCode; +import com.google.api.gax.rpc.InternalException; +import com.google.common.base.Predicate; +import io.grpc.Status; +import io.grpc.Status.Code; +import io.grpc.StatusRuntimeException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@SuppressWarnings("unchecked") +@RunWith(JUnit4.class) +public class IsRetryableInternalErrorTest { + + private Predicate predicate; + + @Before + public void setUp() { + predicate = new IsRetryableInternalError(); + } + + @Test + public void http2ErrorStatusRuntimeExceptionIsRetryable() { + final StatusRuntimeException e = + new StatusRuntimeException( + Status.fromCode(Code.INTERNAL) + .withDescription("INTERNAL: HTTP/2 error code: INTERNAL_ERROR.")); + + assertThat(predicate.apply(e)).isTrue(); + } + + @Test + public void http2ErrorInternalExceptionIsRetryable() { + final InternalException e = + new InternalException( + "INTERNAL: HTTP/2 error code: INTERNAL_ERROR.", + null, + GrpcStatusCode.of(Code.INTERNAL), + false); + + assertThat(predicate.apply(e)).isTrue(); + } + + @Test + public void connectionClosedStatusRuntimeExceptionIsRetryable() { + final StatusRuntimeException e = + new StatusRuntimeException( + Status.fromCode(Code.INTERNAL) + .withDescription("INTERNAL: Connection closed with unknown cause.")); + + assertThat(predicate.apply(e)).isTrue(); + } + + @Test + public void connectionClosedInternalExceptionIsRetryable() { + final InternalException e = + new InternalException( + "INTERNAL: Connection closed with unknown cause.", + null, + GrpcStatusCode.of(Code.INTERNAL), + false); + + assertThat(predicate.apply(e)).isTrue(); + } + + @Test + public void eosStatusRuntimeExceptionIsRetryable() { + final StatusRuntimeException e = + new StatusRuntimeException( + Status.fromCode(Code.INTERNAL) + .withDescription("INTERNAL: Received unexpected EOS on DATA frame from server.")); + + assertThat(predicate.apply(e)).isTrue(); + } + + @Test + public void eosInternalExceptionIsRetryable() { + final InternalException e = + new InternalException( + "INTERNAL: Received unexpected EOS on DATA frame from server.", + null, + GrpcStatusCode.of(Code.INTERNAL), + false); + + assertThat(predicate.apply(e)).isTrue(); + } + + @Test + public void genericInternalStatusRuntimeExceptionIsRetryable() { + final StatusRuntimeException e = + new StatusRuntimeException( + Status.fromCode(Code.INTERNAL).withDescription("INTERNAL: Generic.")); + + assertThat(predicate.apply(e)).isFalse(); + } + + @Test + public void genericInternalExceptionIsNotRetryable() { + final InternalException e = + new InternalException("INTERNAL: Generic.", null, GrpcStatusCode.of(Code.INTERNAL), false); + + assertThat(predicate.apply(e)).isFalse(); + } + + @Test + public void nullIsNotRetryable() { + assertThat(predicate.apply(null)).isFalse(); + } + + @Test + public void genericExceptionIsNotRetryable() { + assertThat(predicate.apply(new Exception())).isFalse(); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IsSslHandshakeExceptionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IsSslHandshakeExceptionTest.java new file mode 100644 index 00000000000..b73c2d6ef1b --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IsSslHandshakeExceptionTest.java @@ -0,0 +1,53 @@ +/* + * 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; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.base.Predicate; +import javax.net.ssl.SSLHandshakeException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@SuppressWarnings("unchecked") +@RunWith(JUnit4.class) +public class IsSslHandshakeExceptionTest { + + private Predicate predicate; + + @Before + public void setUp() { + predicate = new IsSslHandshakeException(); + } + + @Test + public void sslHandshakeExceptionIsTrue() { + assertThat(predicate.apply(new SSLHandshakeException("test"))).isTrue(); + } + + @Test + public void genericExceptionIsNotSslHandshakeException() { + assertThat(predicate.apply(new Exception("test"))).isFalse(); + } + + @Test + public void nullIsNotSslHandshakeException() { + assertThat(predicate.apply(null)).isFalse(); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/PartitionedDmlTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/PartitionedDmlTransactionTest.java index 8a158eb12e6..a38cd71baa1 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/PartitionedDmlTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/PartitionedDmlTransactionTest.java @@ -20,27 +20,19 @@ import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyMap; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; import com.google.api.gax.grpc.GrpcStatusCode; import com.google.api.gax.rpc.AbortedException; +import com.google.api.gax.rpc.InternalException; import com.google.api.gax.rpc.ServerStream; import com.google.api.gax.rpc.UnavailableException; import com.google.cloud.spanner.spi.v1.SpannerRpc; -import com.google.common.base.Stopwatch; import com.google.common.base.Ticker; import com.google.common.collect.ImmutableList; import com.google.protobuf.ByteString; -import com.google.spanner.v1.BeginTransactionRequest; -import com.google.spanner.v1.ExecuteSqlRequest; +import com.google.spanner.v1.*; import com.google.spanner.v1.ExecuteSqlRequest.QueryMode; -import com.google.spanner.v1.PartialResultSet; -import com.google.spanner.v1.ResultSetStats; -import com.google.spanner.v1.Transaction; -import com.google.spanner.v1.TransactionSelector; import io.grpc.Status.Code; import java.util.Collections; import java.util.Iterator; @@ -64,6 +56,10 @@ public class PartitionedDmlTransactionTest { @Mock private SessionImpl session; + @Mock private Ticker ticker; + + private PartitionedDmlTransaction tx; + private final String sessionId = "projects/p/instances/i/databases/d/sessions/s"; private final ByteString txId = ByteString.copyFromUtf8("tx"); private final ByteString resumeToken = ByteString.copyFromUtf8("resume"); @@ -85,6 +81,8 @@ public void setup() { when(session.getOptions()).thenReturn(Collections.EMPTY_MAP); when(rpc.beginTransaction(any(BeginTransactionRequest.class), anyMap())) .thenReturn(Transaction.newBuilder().setId(txId).build()); + + tx = new PartitionedDmlTransaction(session, rpc, ticker); } @Test @@ -98,8 +96,8 @@ public void testExecuteStreamingPartitionedUpdate() { Mockito.eq(executeRequestWithoutResumeToken), anyMap(), any(Duration.class))) .thenReturn(stream); - PartitionedDMLTransaction tx = new PartitionedDMLTransaction(session, rpc); long count = tx.executeStreamingPartitionedUpdate(Statement.of(sql), Duration.ofMinutes(10)); + assertThat(count).isEqualTo(1000L); verify(rpc).beginTransaction(any(BeginTransactionRequest.class), anyMap()); verify(rpc) @@ -127,8 +125,8 @@ public void testExecuteStreamingPartitionedUpdateAborted() { any(ExecuteSqlRequest.class), anyMap(), any(Duration.class))) .thenReturn(stream1, stream2); - PartitionedDMLTransaction tx = new PartitionedDMLTransaction(session, rpc); long count = tx.executeStreamingPartitionedUpdate(Statement.of(sql), Duration.ofMinutes(10)); + assertThat(count).isEqualTo(1000L); verify(rpc, times(2)).beginTransaction(any(BeginTransactionRequest.class), anyMap()); verify(rpc, times(2)) @@ -159,8 +157,8 @@ public void testExecuteStreamingPartitionedUpdateUnavailable() { Mockito.eq(executeRequestWithResumeToken), anyMap(), any(Duration.class))) .thenReturn(stream2); - PartitionedDMLTransaction tx = new PartitionedDMLTransaction(session, rpc); long count = tx.executeStreamingPartitionedUpdate(Statement.of(sql), Duration.ofMinutes(10)); + assertThat(count).isEqualTo(1000L); verify(rpc).beginTransaction(any(BeginTransactionRequest.class), anyMap()); verify(rpc) @@ -186,17 +184,8 @@ public void testExecuteStreamingPartitionedUpdateUnavailableAndThenDeadlineExcee when(rpc.executeStreamingPartitionedDml( Mockito.eq(executeRequestWithoutResumeToken), anyMap(), any(Duration.class))) .thenReturn(stream1); + when(ticker.read()).thenReturn(0L, 1L, TimeUnit.NANOSECONDS.convert(10L, TimeUnit.MINUTES)); - PartitionedDMLTransaction tx = - new PartitionedDMLTransaction(session, rpc) { - @Override - Stopwatch createStopwatchStarted() { - Ticker ticker = mock(Ticker.class); - when(ticker.read()) - .thenReturn(0L, 1L, TimeUnit.NANOSECONDS.convert(10L, TimeUnit.MINUTES)); - return Stopwatch.createStarted(ticker); - } - }; try { tx.executeStreamingPartitionedUpdate(Statement.of(sql), Duration.ofMinutes(10)); fail("missing expected DEADLINE_EXCEEDED exception"); @@ -224,17 +213,8 @@ public void testExecuteStreamingPartitionedUpdateAbortedAndThenDeadlineExceeded( when(rpc.executeStreamingPartitionedDml( Mockito.eq(executeRequestWithoutResumeToken), anyMap(), any(Duration.class))) .thenReturn(stream1); + when(ticker.read()).thenReturn(0L, 1L, TimeUnit.NANOSECONDS.convert(10L, TimeUnit.MINUTES)); - PartitionedDMLTransaction tx = - new PartitionedDMLTransaction(session, rpc) { - @Override - Stopwatch createStopwatchStarted() { - Ticker ticker = mock(Ticker.class); - when(ticker.read()) - .thenReturn(0L, 1L, TimeUnit.NANOSECONDS.convert(10L, TimeUnit.MINUTES)); - return Stopwatch.createStarted(ticker); - } - }; try { tx.executeStreamingPartitionedUpdate(Statement.of(sql), Duration.ofMinutes(10)); fail("missing expected DEADLINE_EXCEEDED exception"); @@ -262,25 +242,17 @@ public void testExecuteStreamingPartitionedUpdateMultipleAbortsUntilDeadlineExce when(rpc.executeStreamingPartitionedDml( Mockito.eq(executeRequestWithoutResumeToken), anyMap(), any(Duration.class))) .thenReturn(stream1); + when(ticker.read()) + .thenAnswer( + new Answer() { + long ticks = 0L; + + @Override + public Long answer(InvocationOnMock invocation) throws Throwable { + return TimeUnit.NANOSECONDS.convert(++ticks, TimeUnit.MINUTES); + } + }); - PartitionedDMLTransaction tx = - new PartitionedDMLTransaction(session, rpc) { - long ticks = 0L; - - @Override - Stopwatch createStopwatchStarted() { - Ticker ticker = mock(Ticker.class); - when(ticker.read()) - .thenAnswer( - new Answer() { - @Override - public Long answer(InvocationOnMock invocation) throws Throwable { - return TimeUnit.NANOSECONDS.convert(++ticks, TimeUnit.MINUTES); - } - }); - return Stopwatch.createStarted(ticker); - } - }; try { tx.executeStreamingPartitionedUpdate(Statement.of(sql), Duration.ofMinutes(10)); fail("missing expected DEADLINE_EXCEEDED exception"); @@ -295,4 +267,72 @@ public Long answer(InvocationOnMock invocation) throws Throwable { Mockito.eq(executeRequestWithoutResumeToken), anyMap(), any(Duration.class)); } } + + @Test + public void testExecuteStreamingPartitionedUpdateUnexpectedEOS() { + ResultSetStats stats = ResultSetStats.newBuilder().setRowCountLowerBound(1000L).build(); + PartialResultSet p1 = PartialResultSet.newBuilder().setResumeToken(resumeToken).build(); + PartialResultSet p2 = PartialResultSet.newBuilder().setStats(stats).build(); + ServerStream stream1 = mock(ServerStream.class); + Iterator iterator = mock(Iterator.class); + when(iterator.hasNext()).thenReturn(true, true, false); + when(iterator.next()) + .thenReturn(p1) + .thenThrow( + new InternalException( + "INTERNAL: Received unexpected EOS on DATA frame from server.", + null, + GrpcStatusCode.of(Code.INTERNAL), + true)); + when(stream1.iterator()).thenReturn(iterator); + ServerStream stream2 = mock(ServerStream.class); + when(stream2.iterator()).thenReturn(ImmutableList.of(p1, p2).iterator()); + when(rpc.executeStreamingPartitionedDml( + Mockito.eq(executeRequestWithoutResumeToken), anyMap(), any(Duration.class))) + .thenReturn(stream1); + when(rpc.executeStreamingPartitionedDml( + Mockito.eq(executeRequestWithResumeToken), anyMap(), any(Duration.class))) + .thenReturn(stream2); + + PartitionedDmlTransaction tx = new PartitionedDmlTransaction(session, rpc, ticker); + long count = tx.executeStreamingPartitionedUpdate(Statement.of(sql), Duration.ofMinutes(10)); + + assertThat(count).isEqualTo(1000L); + verify(rpc).beginTransaction(any(BeginTransactionRequest.class), anyMap()); + verify(rpc) + .executeStreamingPartitionedDml( + Mockito.eq(executeRequestWithoutResumeToken), anyMap(), any(Duration.class)); + verify(rpc) + .executeStreamingPartitionedDml( + Mockito.eq(executeRequestWithResumeToken), anyMap(), any(Duration.class)); + } + + @Test + public void testExecuteStreamingPartitionedUpdateGenericInternalException() { + PartialResultSet p1 = PartialResultSet.newBuilder().setResumeToken(resumeToken).build(); + ServerStream stream1 = mock(ServerStream.class); + Iterator iterator = mock(Iterator.class); + when(iterator.hasNext()).thenReturn(true, true, false); + when(iterator.next()) + .thenReturn(p1) + .thenThrow( + new InternalException( + "INTERNAL: Error", null, GrpcStatusCode.of(Code.INTERNAL), false)); + when(stream1.iterator()).thenReturn(iterator); + when(rpc.executeStreamingPartitionedDml( + Mockito.eq(executeRequestWithoutResumeToken), anyMap(), any(Duration.class))) + .thenReturn(stream1); + + try { + PartitionedDmlTransaction tx = new PartitionedDmlTransaction(session, rpc, ticker); + tx.executeStreamingPartitionedUpdate(Statement.of(sql), Duration.ofMinutes(10)); + fail("missing expected INTERNAL exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INTERNAL); + verify(rpc).beginTransaction(any(BeginTransactionRequest.class), anyMap()); + verify(rpc) + .executeStreamingPartitionedDml( + Mockito.eq(executeRequestWithoutResumeToken), anyMap(), any(Duration.class)); + } + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITReadOnlySpannerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITReadOnlySpannerTest.java index db833492323..d6c89c65d93 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITReadOnlySpannerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITReadOnlySpannerTest.java @@ -16,6 +16,7 @@ package com.google.cloud.spanner.connection.it; +import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; @@ -169,7 +170,7 @@ public void testStatementTimeoutAutocommit() { @Test public void testAnalyzeQuery() { - assumeFalse("analyze query is not supported on the emulator", env.getTestHelper().isEmulator()); + assumeFalse("analyze query is not supported on the emulator", isUsingEmulator()); try (ITConnection connection = createConnection()) { for (QueryAnalyzeMode mode : QueryAnalyzeMode.values()) { try (ResultSet rs = diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITReadWriteAutocommitSpannerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITReadWriteAutocommitSpannerTest.java index 6b85d8b44be..5963cacb902 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITReadWriteAutocommitSpannerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITReadWriteAutocommitSpannerTest.java @@ -16,6 +16,7 @@ package com.google.cloud.spanner.connection.it; +import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; @@ -76,7 +77,7 @@ public void test02_WriteMutation() { public void test03_MultipleStatements_WithTimeouts() { assumeFalse( "Rolling back a transaction while an update statement is still in flight can cause the transaction to remain active on the emulator", - env.getTestHelper().isEmulator()); + isUsingEmulator()); try (ITConnection connection = createConnection()) { // do an insert that should succeed assertThat( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITSqlMusicScriptTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITSqlMusicScriptTest.java index 5f239f2c9ec..a6b4fc88734 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITSqlMusicScriptTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITSqlMusicScriptTest.java @@ -16,6 +16,7 @@ package com.google.cloud.spanner.connection.it; +import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -59,9 +60,7 @@ public void test01_RunScript() throws Exception { @Test public void test02_RunAbortedTest() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); final long SINGER_ID = 2L; final long VENUE_ID = 68L; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITSqlScriptTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITSqlScriptTest.java index 9ff245ffe58..5a1e9bb6282 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITSqlScriptTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITSqlScriptTest.java @@ -16,6 +16,8 @@ package com.google.cloud.spanner.connection.it; +import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; + import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.ParallelIntegrationTest; import com.google.cloud.spanner.SpannerException; @@ -76,7 +78,7 @@ public void test02_InsertTestData() throws Exception { INSERT_AND_VERIFY_TEST_DATA, SqlScriptVerifier.class); } catch (SpannerException e) { - if (env.getTestHelper().isEmulator() && e.getErrorCode() == ErrorCode.ALREADY_EXISTS) { + if (isUsingEmulator() && e.getErrorCode() == ErrorCode.ALREADY_EXISTS) { // Errors in a transaction are 'sticky' on the emulator, so any query in the same // transaction will return the same error as the error generated by a previous (update) // statement. @@ -102,7 +104,7 @@ public void test04_TestGetCommitTimestamp() throws Exception { TEST_GET_COMMIT_TIMESTAMP, SqlScriptVerifier.class); } catch (SpannerException e) { - if (env.getTestHelper().isEmulator() && e.getErrorCode() == ErrorCode.INVALID_ARGUMENT) { + if (isUsingEmulator() && e.getErrorCode() == ErrorCode.INVALID_ARGUMENT) { // Errors in a transaction are 'sticky' on the emulator, so any query in the same // transaction will return the same error as the error generated by a previous statement. } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITTransactionRetryTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITTransactionRetryTest.java index 1259e2ab8d3..c1567496bc1 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITTransactionRetryTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITTransactionRetryTest.java @@ -16,6 +16,7 @@ package com.google.cloud.spanner.connection.it; +import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -488,9 +489,7 @@ public void testAbortWithResultSetFullyConsumed() { @Test public void testAbortWithConcurrentInsert() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { @@ -525,9 +524,7 @@ public void testAbortWithConcurrentInsert() { @Test public void testAbortWithConcurrentDelete() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); AbortInterceptor interceptor = new AbortInterceptor(0); // first insert two test records try (ITConnection connection = createConnection()) { @@ -566,9 +563,7 @@ public void testAbortWithConcurrentDelete() { @Test public void testAbortWithConcurrentUpdate() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); AbortInterceptor interceptor = new AbortInterceptor(0); // first insert two test records try (ITConnection connection = createConnection()) { @@ -612,9 +607,7 @@ public void testAbortWithConcurrentUpdate() { */ @Test public void testAbortWithUnseenConcurrentInsert() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { @@ -662,9 +655,7 @@ public void testAbortWithUnseenConcurrentInsert() { */ @Test public void testAbortWithUnseenConcurrentInsertAbortOnNext() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); // no calls to next(), this should succeed assertThat(testAbortWithUnseenConcurrentInsertAbortOnNext(0) >= 1, is(true)); // 1 call to next() should also succeed, as there were 2 records in the original result set @@ -686,9 +677,7 @@ public void testAbortWithUnseenConcurrentInsertAbortOnNext() { private int testAbortWithUnseenConcurrentInsertAbortOnNext(int callsToNext) throws AbortedDueToConcurrentModificationException { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); int retries = 0; clearTable(); clearStatistics(); @@ -748,9 +737,7 @@ private int testAbortWithUnseenConcurrentInsertAbortOnNext(int callsToNext) */ @Test public void testAbortWithConcurrentInsertAndContinue() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { @@ -960,9 +947,7 @@ protected boolean shouldAbort(String statement, ExecutionStep step) { */ @Test public void testNestedAbortWithConcurrentInsert() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); AbortInterceptor interceptor = new AbortInterceptor(0) { private boolean alreadyAborted = false; @@ -1025,9 +1010,7 @@ protected boolean shouldAbort(String statement, ExecutionStep step) { */ @Test public void testAbortWithDifferentUpdateCount() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); AbortInterceptor interceptor = new AbortInterceptor(0); // first insert two test records try (ITConnection connection = createConnection()) { @@ -1074,8 +1057,7 @@ public void testAbortWithDifferentUpdateCount() { @Test public void testAbortWithExceptionOnSelect() { assumeFalse( - "resume after error in transaction is not supported on the emulator", - env.getTestHelper().isEmulator()); + "resume after error in transaction is not supported on the emulator", isUsingEmulator()); AbortInterceptor interceptor = new AbortInterceptor(0); // first insert two test records try (ITConnection connection = createConnection()) { @@ -1125,9 +1107,7 @@ public void testAbortWithExceptionOnSelect() { */ @Test public void testAbortWithExceptionOnSelectAndConcurrentModification() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); boolean abortedDueToConcurrentModification = false; AbortInterceptor interceptor = new AbortInterceptor(0); // first insert two test records @@ -1195,9 +1175,7 @@ public void testAbortWithExceptionOnSelectAndConcurrentModification() { */ @Test public void testAbortWithExceptionOnInsertAndConcurrentModification() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); boolean abortedDueToConcurrentModification = false; AbortInterceptor interceptor = new AbortInterceptor(0); // first insert two test records @@ -1264,9 +1242,7 @@ public void testAbortWithExceptionOnInsertAndConcurrentModification() { */ @Test public void testAbortWithDroppedTableConcurrentModification() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); boolean abortedDueToConcurrentModification = false; AbortInterceptor interceptor = new AbortInterceptor(0); // first insert two test records @@ -1329,9 +1305,7 @@ public void testAbortWithDroppedTableConcurrentModification() { */ @Test public void testAbortWithInsertOnDroppedTableConcurrentModification() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); boolean abortedDueToConcurrentModification = false; AbortInterceptor interceptor = new AbortInterceptor(0); // first insert two test records @@ -1391,9 +1365,7 @@ public void testAbortWithInsertOnDroppedTableConcurrentModification() { */ @Test public void testAbortWithCursorHalfwayDroppedTableConcurrentModification() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); boolean abortedDueToConcurrentModification = false; AbortInterceptor interceptor = new AbortInterceptor(0); // first insert two test records @@ -1546,9 +1518,7 @@ public void testRetryHighAbortRate() { @Test public void testAbortWithConcurrentInsertOnEmptyTable() { - assumeFalse( - "concurrent transactions are not supported on the emulator", - env.getTestHelper().isEmulator()); + assumeFalse("concurrent transactions are not supported on the emulator", isUsingEmulator()); AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { 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 a2239aa3b98..721536cb6b3 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 @@ -18,7 +18,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; -import static org.junit.Assume.assumeFalse; import com.google.api.core.ApiFuture; import com.google.cloud.spanner.AsyncResultSet; @@ -279,9 +278,6 @@ public void columnNotFound() throws Exception { @Test public void asyncRunnerFireAndForgetInvalidUpdate() throws Exception { - assumeFalse( - "errors in read/write transactions on emulator are sticky", - env.getTestHelper().isEmulator()); try { assertThat(client.singleUse().readRow("TestTable", Key.of("k999"), ALL_COLUMNS)).isNull(); AsyncRunner runner = client.runAsync(); 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 2a35a170838..0fbd0702ec5 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 @@ -16,6 +16,7 @@ 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; @@ -93,7 +94,7 @@ public class ITBackupTest { @BeforeClass public static void doNotRunOnEmulator() { - assumeFalse("backups are not supported on the emulator", env.getTestHelper().isEmulator()); + assumeFalse("backups are not supported on the emulator", isUsingEmulator()); } @Before diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java index 3031b0edd85..1e00015cdfb 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java @@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; -import static org.junit.Assume.assumeFalse; import com.google.cloud.spanner.AbortedException; import com.google.cloud.spanner.Database; @@ -61,10 +60,6 @@ public class ITClosedSessionTest { @BeforeClass public static void setUpDatabase() { - // TODO: Enable when the emulator returns ResourceInfo for Session not found errors. - assumeFalse( - "Emulator does not return ResourceInfo for Session not found errors", - env.getTestHelper().isEmulator()); // Empty database. db = env.getTestHelper().createTestDatabase(); client = (DatabaseClientWithClosedSessionImpl) env.getTestHelper().getDatabaseClient(db); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITCommitTimestampTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITCommitTimestampTest.java index 84d1a675597..187f1e9f129 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITCommitTimestampTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITCommitTimestampTest.java @@ -18,7 +18,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; -import static org.junit.Assume.assumeFalse; import com.google.cloud.Timestamp; import com.google.cloud.spanner.Database; @@ -325,10 +324,7 @@ public void interleavedTableHierarchy1() { .build())); fail("missing expected exception"); } catch (SpannerException e) { - // TODO: Remove when the emulator returns the same error code as Cloud Spanner. - if (!env.getTestHelper().isEmulator()) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - } + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); } } @@ -336,8 +332,6 @@ public void interleavedTableHierarchy1() { // allowed if child tables are not allow_commmit_timestamp=true @Test public void interleavedTableHierarchy2() { - // TODO: Remove the following line once the emulator is as strict as Cloud Spanner. - assumeFalse("The emulator allows this situation", env.getTestHelper().isEmulator()); Database db = testHelper.createTestDatabase( "CREATE TABLE T1 (ts TIMESTAMP OPTIONS (allow_commit_timestamp = true)) " diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITDatabaseAdminTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITDatabaseAdminTest.java index f66154d66c2..167ab97ce4f 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITDatabaseAdminTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITDatabaseAdminTest.java @@ -16,6 +16,7 @@ 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; @@ -265,8 +266,7 @@ public void onClose(Status status, Metadata metadata) { @Test public void testRetryNonIdempotentRpcsReturningLongRunningOperations() throws Exception { assumeFalse( - "Querying long-running operations is not supported on the emulator", - env.getTestHelper().isEmulator()); + "Querying long-running operations is not supported on the emulator", isUsingEmulator()); // RPCs that return a long-running operation such as CreateDatabase, CreateBackup and // RestoreDatabase are non-idempotent and can normally not be automatically retried in case of a diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITDatabaseTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITDatabaseTest.java index 450c7463c50..49ce8f18f07 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITDatabaseTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITDatabaseTest.java @@ -16,6 +16,7 @@ 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; @@ -64,8 +65,7 @@ public void databaseDeletedTest() throws Exception { // TODO: Remove the following line once the emulator returns ResourceInfo for Database not found // errors. assumeFalse( - "Emulator does not return ResourceInfo for Database not found errors", - env.getTestHelper().isEmulator()); + "Emulator does not return ResourceInfo for Database not found errors", isUsingEmulator()); // Create a test db, do a query, then delete it and verify that it returns // DatabaseNotFoundExceptions. @@ -135,8 +135,7 @@ public void instanceNotFound() { // TODO: Remove the following line when the emulator returns ResourceInfo for Instance not found // errors. assumeFalse( - "Emulator does not return ResourceInfo for Instance not found errors", - env.getTestHelper().isEmulator()); + "Emulator does not return ResourceInfo for Instance not found errors", isUsingEmulator()); InstanceId testId = env.getTestHelper().getInstanceId(); InstanceId nonExistingInstanceId = diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITInstanceAdminTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITInstanceAdminTest.java index 68e4e615a39..340f0a5958c 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITInstanceAdminTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITInstanceAdminTest.java @@ -16,6 +16,7 @@ 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.Assume.assumeFalse; @@ -78,8 +79,7 @@ public void listInstances() { @Test public void updateInstance() throws Exception { - assumeFalse( - "The emulator does not support updating instances", env.getTestHelper().isEmulator()); + assumeFalse("The emulator does not support updating instances", isUsingEmulator()); Instance instance = instanceClient.getInstance(env.getTestHelper().getInstanceId().getInstance()); @@ -108,8 +108,7 @@ public void updateInstance() throws Exception { @Test public void updateInstanceViaEntity() throws Exception { - assumeFalse( - "The emulator does not support updating instances", env.getTestHelper().isEmulator()); + assumeFalse("The emulator does not support updating instances", isUsingEmulator()); Instance instance = instanceClient.getInstance(env.getTestHelper().getInstanceId().getInstance()); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryOptionsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryOptionsTest.java index 30cfa80c458..910effb0cd5 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryOptionsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryOptionsTest.java @@ -16,6 +16,7 @@ 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; @@ -48,7 +49,7 @@ public class ITQueryOptionsTest { @BeforeClass public static void setUpDatabase() { - assumeFalse("Emulator ignores query options", env.getTestHelper().isEmulator()); + assumeFalse("Emulator ignores query options", isUsingEmulator()); // Empty database. db = diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java index b54984362d7..4bb788031b9 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java @@ -17,6 +17,7 @@ package com.google.cloud.spanner.it; import static com.google.cloud.spanner.Type.StructField; +import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; import static org.junit.Assert.fail; @@ -495,7 +496,7 @@ public void bindDateArrayNull() { @Test public void unsupportedSelectStructValue() { - assumeFalse("The emulator accepts this query", env.getTestHelper().isEmulator()); + assumeFalse("The emulator accepts this query", isUsingEmulator()); Struct p = structValue(); try { execute(Statement.newBuilder("SELECT @p").bind("p").to(p).build(), p.getType()); @@ -511,8 +512,7 @@ public void unsupportedSelectStructValue() { @Test public void unsupportedSelectArrayStructValue() { assumeFalse( - "Emulator evaluates this expression differently than Cloud Spanner", - env.getTestHelper().isEmulator()); + "Emulator evaluates this expression differently than Cloud Spanner", isUsingEmulator()); Struct p = structValue(); try { @@ -829,7 +829,7 @@ public void queryRealTable() { @Test public void analyzePlan() { - assumeFalse("Emulator does not support Analyze Plan", env.getTestHelper().isEmulator()); + assumeFalse("Emulator does not support Analyze Plan", isUsingEmulator()); Statement statement = Statement.of("SELECT 1 AS column UNION ALL SELECT 2"); ResultSet resultSet = @@ -844,7 +844,7 @@ public void analyzePlan() { @Test public void analyzeProfile() { - assumeFalse("Emulator does not support Analyze Profile", env.getTestHelper().isEmulator()); + assumeFalse("Emulator does not support Analyze Profile", isUsingEmulator()); Statement statement = Statement.of("SELECT 1 AS column UNION ALL SELECT 2 AS column ORDER BY column"); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerAsyncTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerAsyncTest.java index c802493dec5..ab9acd90156 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerAsyncTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerAsyncTest.java @@ -16,6 +16,7 @@ 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.assertNotNull; import static org.junit.Assert.fail; @@ -218,7 +219,7 @@ public void testAbortAndRetry() throws InterruptedException, ExecutionException assumeFalse( "Emulator does not support more than 1 simultanous transaction. " + "This test would therefore loop indefinetly on the emulator.", - env.getTestHelper().isEmulator()); + isUsingEmulator()); client.write( Arrays.asList( 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 281977af6a0..06087b9afec 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 @@ -16,6 +16,7 @@ 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.assertNotNull; import static org.junit.Assert.fail; @@ -151,7 +152,7 @@ public void abortAndRetry() throws InterruptedException { assumeFalse( "Emulator does not support more than 1 simultanous transaction. " + "This test would therefore loop indefinetly on the emulator.", - env.getTestHelper().isEmulator()); + isUsingEmulator()); client.write( Arrays.asList( 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 5e3c1483e7d..ed3450893d9 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 @@ -18,6 +18,7 @@ import static com.google.cloud.spanner.SpannerExceptionFactory.newSpannerException; import static com.google.cloud.spanner.TransactionRunner.TransactionCallable; +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; @@ -153,9 +154,7 @@ public void run() { @Test public void basicsUsingRead() throws InterruptedException { - assumeFalse( - "Emulator does not support multiple parallel transactions", - env.getTestHelper().isEmulator()); + assumeFalse("Emulator does not support multiple parallel transactions", isUsingEmulator()); doBasicsTest( new ReadStrategy() { @@ -168,9 +167,7 @@ public Struct read(ReadContext ctx, String key) { @Test public void basicsUsingQuery() throws InterruptedException { - assumeFalse( - "Emulator does not support multiple parallel transactions", - env.getTestHelper().isEmulator()); + assumeFalse("Emulator does not support multiple parallel transactions", isUsingEmulator()); doBasicsTest( new ReadStrategy() { @@ -251,9 +248,7 @@ public Void run(TransactionContext transaction) { @Test public void readAbort() throws Exception { - assumeFalse( - "Emulator does not support multiple parallel transactions", - env.getTestHelper().isEmulator()); + assumeFalse("Emulator does not support multiple parallel transactions", isUsingEmulator()); final String key1 = uniqueKey(); final String key2 = uniqueKey(); @@ -496,9 +491,7 @@ public Void run(TransactionContext transaction) throws SpannerException { @Test public void nestedTxnSucceedsWhenAllowed() { - assumeFalse( - "Emulator does not support multiple parallel transactions", - env.getTestHelper().isEmulator()); + assumeFalse("Emulator does not support multiple parallel transactions", isUsingEmulator()); client .readWriteTransaction() 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 f64fdae5124..189f9ec6d37 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 @@ -20,6 +20,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assume.assumeTrue; import com.google.api.core.ApiFunction; import com.google.auth.oauth2.AccessToken; @@ -71,6 +72,7 @@ import java.util.regex.Pattern; import org.junit.After; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -128,6 +130,13 @@ public class GapicSpannerRpcTest { private InetSocketAddress address; private final Map optionsMap = new HashMap<>(); + @BeforeClass + public static void checkNotEmulator() { + assumeTrue( + "Skip tests when emulator is enabled as this test interferes with the check whether the emulator is running", + System.getenv("SPANNER_EMULATOR_HOST") == null); + } + @Before public void startServer() throws IOException { mockSpanner = new MockSpannerServiceImpl(); diff --git a/grpc-google-cloud-spanner-admin-database-v1/pom.xml b/grpc-google-cloud-spanner-admin-database-v1/pom.xml index e200ec0ef4c..926c07be5fd 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 - 1.59.0 + 1.60.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 - 1.59.0 + 1.60.0 diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml index a4e973e2b5c..5be79f0de0a 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 - 1.59.0 + 1.60.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 - 1.59.0 + 1.60.0 diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml index 72516223dde..0abde3a9287 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 - 1.59.0 + 1.60.0 grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 1.59.0 + 1.60.0 diff --git a/pom.xml b/pom.xml index 96e1e46028b..93214c9ca78 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-spanner-parent pom - 1.59.0 + 1.60.0 Google Cloud Spanner Parent https://github.com/googleapis/java-spanner @@ -70,43 +70,43 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 1.59.0 + 1.60.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 1.59.0 + 1.60.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 1.59.0 + 1.60.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 1.59.0 + 1.60.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 1.59.0 + 1.60.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 1.59.0 + 1.60.0 com.google.cloud google-cloud-spanner - 1.59.0 + 1.60.0 com.google.cloud google-cloud-shared-dependencies - 0.8.3 + 0.8.6 pom import diff --git a/proto-google-cloud-spanner-admin-database-v1/pom.xml b/proto-google-cloud-spanner-admin-database-v1/pom.xml index 1cae1e4f9ca..a92d7ce2a0c 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 - 1.59.0 + 1.60.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 - 1.59.0 + 1.60.0 diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml index 941875a719f..e143699c791 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 - 1.59.0 + 1.60.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 - 1.59.0 + 1.60.0 diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml index c8ded9c9985..f8dc250f39c 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 - 1.59.0 + 1.60.0 proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 1.59.0 + 1.60.0 diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index ea47e935d24..56a6276d553 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -29,7 +29,7 @@ com.google.cloud google-cloud-spanner - 1.58.0 + 1.59.0 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index fe2f4c0d854..7c3aa1def02 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -28,7 +28,7 @@ com.google.cloud google-cloud-spanner - 1.59.0 + 1.60.0 diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 9452eb4d397..1ba7060b506 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -30,7 +30,7 @@ com.google.cloud libraries-bom - 8.1.0 + 9.1.0 pom import diff --git a/synth.metadata b/synth.metadata index 2907acc5f79..7b6e5e97d64 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,22 +4,22 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "6ee83b5ce2e1c2a26ca83450f3a6b77f7c327b52" + "sha": "a9f90ad20698fbabca6248d3f530e44f7c2ef86b" } }, { "git": { "name": "googleapis", "remote": "https://github.com/googleapis/googleapis.git", - "sha": "59f97e6044a1275f83427ab7962a154c00d915b5", - "internalRef": "321058159" + "sha": "8f2eda119e11c8bd0c189b545da18bba9019c83e", + "internalRef": "322293383" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "4f2c9f752a94042472fc03c5bd9e06e89817d2bd" + "sha": "f8823dec98277a9516f2fb6fae9f58b3a59a23e1" } } ], @@ -51,5 +51,289 @@ "generator": "bazel" } } + ], + "generatedFiles": [ + ".github/CODEOWNERS", + ".github/ISSUE_TEMPLATE/bug_report.md", + ".github/ISSUE_TEMPLATE/feature_request.md", + ".github/ISSUE_TEMPLATE/support_request.md", + ".github/PULL_REQUEST_TEMPLATE.md", + ".github/release-please.yml", + ".github/trusted-contribution.yml", + ".github/workflows/ci.yaml", + ".kokoro/build.bat", + ".kokoro/build.sh", + ".kokoro/coerce_logs.sh", + ".kokoro/common.cfg", + ".kokoro/common.sh", + ".kokoro/continuous/java8.cfg", + ".kokoro/dependencies.sh", + ".kokoro/linkage-monitor.sh", + ".kokoro/nightly/integration.cfg", + ".kokoro/nightly/java11.cfg", + ".kokoro/nightly/java7.cfg", + ".kokoro/nightly/java8-osx.cfg", + ".kokoro/nightly/java8-win.cfg", + ".kokoro/nightly/java8.cfg", + ".kokoro/nightly/samples.cfg", + ".kokoro/populate-secrets.sh", + ".kokoro/presubmit/clirr.cfg", + ".kokoro/presubmit/dependencies.cfg", + ".kokoro/presubmit/integration.cfg", + ".kokoro/presubmit/java11.cfg", + ".kokoro/presubmit/java7.cfg", + ".kokoro/presubmit/java8-osx.cfg", + ".kokoro/presubmit/java8-win.cfg", + ".kokoro/presubmit/java8.cfg", + ".kokoro/presubmit/linkage-monitor.cfg", + ".kokoro/presubmit/lint.cfg", + ".kokoro/presubmit/samples.cfg", + ".kokoro/release/bump_snapshot.cfg", + ".kokoro/release/common.cfg", + ".kokoro/release/common.sh", + ".kokoro/release/drop.cfg", + ".kokoro/release/drop.sh", + ".kokoro/release/promote.cfg", + ".kokoro/release/promote.sh", + ".kokoro/release/publish_javadoc.cfg", + ".kokoro/release/publish_javadoc.sh", + ".kokoro/release/snapshot.cfg", + ".kokoro/release/snapshot.sh", + ".kokoro/release/stage.cfg", + ".kokoro/release/stage.sh", + ".kokoro/trampoline.sh", + "CODE_OF_CONDUCT.md", + "CONTRIBUTING.md", + "LICENSE", + "README.md", + "codecov.yaml", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClient.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminSettings.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/package-info.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/DatabaseAdminStub.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/DatabaseAdminStubSettings.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/GrpcDatabaseAdminCallableFactory.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/GrpcDatabaseAdminStub.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/InstanceAdminClient.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/InstanceAdminSettings.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/package-info.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/stub/GrpcInstanceAdminCallableFactory.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/stub/GrpcInstanceAdminStub.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/stub/InstanceAdminStub.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/instance/v1/stub/InstanceAdminStubSettings.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/SpannerClient.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/SpannerSettings.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/package-info.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/stub/GrpcSpannerCallableFactory.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/stub/GrpcSpannerStub.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/stub/SpannerStub.java", + "google-cloud-spanner/src/main/java/com/google/cloud/spanner/v1/stub/SpannerStubSettings.java", + "google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClientTest.java", + "google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/MockDatabaseAdmin.java", + "google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/MockDatabaseAdminImpl.java", + "google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/instance/v1/InstanceAdminClientTest.java", + "google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/instance/v1/MockInstanceAdmin.java", + "google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/instance/v1/MockInstanceAdminImpl.java", + "google-cloud-spanner/src/test/java/com/google/cloud/spanner/v1/MockSpanner.java", + "google-cloud-spanner/src/test/java/com/google/cloud/spanner/v1/MockSpannerImpl.java", + "google-cloud-spanner/src/test/java/com/google/cloud/spanner/v1/SpannerClientTest.java", + "grpc-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DatabaseAdminGrpc.java", + "grpc-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/InstanceAdminGrpc.java", + "grpc-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SpannerGrpc.java", + "java.header", + "license-checks.xml", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/Backup.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupInfo.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupInfoOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupName.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/BackupProto.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CommonProto.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateBackupMetadata.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateBackupMetadataOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateBackupRequest.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateBackupRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateDatabaseMetadata.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateDatabaseMetadataOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateDatabaseRequest.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/CreateDatabaseRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/Database.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DatabaseName.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DatabaseOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DeleteBackupRequest.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DeleteBackupRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DropDatabaseRequest.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DropDatabaseRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetBackupRequest.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetBackupRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetDatabaseDdlRequest.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetDatabaseDdlRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetDatabaseDdlResponse.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetDatabaseDdlResponseOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetDatabaseRequest.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/GetDatabaseRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/InstanceName.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupOperationsRequest.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupOperationsRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupOperationsResponse.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupOperationsResponseOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupsRequest.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupsRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupsResponse.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListBackupsResponseOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabaseOperationsRequest.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabaseOperationsRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabaseOperationsResponse.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabaseOperationsResponseOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabasesRequest.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabasesRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabasesResponse.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/ListDatabasesResponseOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/OperationProgress.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/OperationProgressOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/OptimizeRestoredDatabaseMetadata.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/OptimizeRestoredDatabaseMetadataOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreDatabaseMetadata.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreDatabaseMetadataOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreDatabaseRequest.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreDatabaseRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreInfo.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreInfoOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/RestoreSourceType.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SpannerDatabaseAdminProto.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/UpdateBackupRequest.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/UpdateBackupRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/UpdateDatabaseDdlMetadata.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/UpdateDatabaseDdlMetadataOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/UpdateDatabaseDdlRequest.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/UpdateDatabaseDdlRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/backup.proto", + "proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/common.proto", + "proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/spanner_database_admin.proto", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/CreateInstanceMetadata.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/CreateInstanceMetadataOrBuilder.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/CreateInstanceRequest.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/CreateInstanceRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/DeleteInstanceRequest.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/DeleteInstanceRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/GetInstanceConfigRequest.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/GetInstanceConfigRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/GetInstanceRequest.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/GetInstanceRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/Instance.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/InstanceConfig.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/InstanceConfigName.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/InstanceConfigOrBuilder.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/InstanceName.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/InstanceOrBuilder.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/ListInstanceConfigsRequest.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/ListInstanceConfigsRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/ListInstanceConfigsResponse.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/ListInstanceConfigsResponseOrBuilder.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/ListInstancesRequest.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/ListInstancesRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/ListInstancesResponse.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/ListInstancesResponseOrBuilder.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/ProjectName.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/ReplicaInfo.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/ReplicaInfoOrBuilder.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/SpannerInstanceAdminProto.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/UpdateInstanceMetadata.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/UpdateInstanceMetadataOrBuilder.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/UpdateInstanceRequest.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/java/com/google/spanner/admin/instance/v1/UpdateInstanceRequestOrBuilder.java", + "proto-google-cloud-spanner-admin-instance-v1/src/main/proto/google/spanner/admin/instance/v1/spanner_instance_admin.proto", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/BatchCreateSessionsRequest.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/BatchCreateSessionsRequestOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/BatchCreateSessionsResponse.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/BatchCreateSessionsResponseOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/BeginTransactionRequest.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/BeginTransactionRequestOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequest.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequestOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponse.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponseOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CreateSessionRequest.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CreateSessionRequestOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/DatabaseName.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/DeleteSessionRequest.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/DeleteSessionRequestOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ExecuteBatchDmlRequest.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ExecuteBatchDmlRequestOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ExecuteBatchDmlResponse.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ExecuteBatchDmlResponseOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ExecuteSqlRequest.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ExecuteSqlRequestOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/GetSessionRequest.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/GetSessionRequestOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/KeyRange.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/KeyRangeOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/KeySet.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/KeySetOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/KeysProto.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ListSessionsRequest.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ListSessionsRequestOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ListSessionsResponse.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ListSessionsResponseOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/Mutation.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/MutationOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/MutationProto.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/PartialResultSet.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/PartialResultSetOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/Partition.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/PartitionOptions.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/PartitionOptionsOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/PartitionOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/PartitionQueryRequest.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/PartitionQueryRequestOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/PartitionReadRequest.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/PartitionReadRequestOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/PartitionResponse.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/PartitionResponseOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/PlanNode.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/PlanNodeOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/QueryPlan.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/QueryPlanOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/QueryPlanProto.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ReadRequest.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ReadRequestOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ResultSet.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ResultSetMetadata.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ResultSetMetadataOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ResultSetOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ResultSetProto.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ResultSetStats.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/ResultSetStatsOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/RollbackRequest.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/RollbackRequestOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/Session.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SessionName.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SessionOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SpannerProto.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/StructType.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/StructTypeOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/Transaction.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TransactionOptions.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TransactionOptionsOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TransactionOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TransactionProto.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TransactionSelector.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TransactionSelectorOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/Type.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeCode.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeOrBuilder.java", + "proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeProto.java", + "proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/keys.proto", + "proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/mutation.proto", + "proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/query_plan.proto", + "proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/result_set.proto", + "proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/spanner.proto", + "proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/transaction.proto", + "proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/type.proto", + "renovate.json", + "samples/install-without-bom/pom.xml", + "samples/pom.xml", + "samples/snapshot/pom.xml", + "samples/snippets/pom.xml" ] } \ No newline at end of file diff --git a/versions.txt b/versions.txt index 4bb29650602..3345caf8237 100644 --- a/versions.txt +++ b/versions.txt @@ -1,10 +1,10 @@ # Format: # module:released-version:current-version -proto-google-cloud-spanner-admin-instance-v1:1.59.0:1.59.0 -proto-google-cloud-spanner-v1:1.59.0:1.59.0 -proto-google-cloud-spanner-admin-database-v1:1.59.0:1.59.0 -grpc-google-cloud-spanner-v1:1.59.0:1.59.0 -grpc-google-cloud-spanner-admin-instance-v1:1.59.0:1.59.0 -grpc-google-cloud-spanner-admin-database-v1:1.59.0:1.59.0 -google-cloud-spanner:1.59.0:1.59.0 \ No newline at end of file +proto-google-cloud-spanner-admin-instance-v1:1.60.0:1.60.0 +proto-google-cloud-spanner-v1:1.60.0:1.60.0 +proto-google-cloud-spanner-admin-database-v1:1.60.0:1.60.0 +grpc-google-cloud-spanner-v1:1.60.0:1.60.0 +grpc-google-cloud-spanner-admin-instance-v1:1.60.0:1.60.0 +grpc-google-cloud-spanner-admin-database-v1:1.60.0:1.60.0 +google-cloud-spanner:1.60.0:1.60.0 \ No newline at end of file