diff --git a/.github/workflows/unmanaged_dependency_check.yaml b/.github/workflows/unmanaged_dependency_check.yaml new file mode 100644 index 00000000000..8a56894c12d --- /dev/null +++ b/.github/workflows/unmanaged_dependency_check.yaml @@ -0,0 +1,22 @@ +on: + pull_request: +name: Unmanaged dependency check +jobs: + unmanaged_dependency_check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 11 + - name: Install modules + shell: bash + run: | + # No argument to build.sh installs the modules in local Maven + # repository + .kokoro/build.sh + - name: Unmanaged dependency check + uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@unmanaged-dependencies-check-latest + with: + bom-path: google-cloud-spanner-bom/pom.xml diff --git a/.gitignore b/.gitignore index ccf024c145b..0141aa5d4c8 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ nosetests.xml .settings .DS_Store .classpath +.tool-versions # Built documentation docs/ diff --git a/.kokoro/release/bump_snapshot.cfg b/.kokoro/release/bump_snapshot.cfg deleted file mode 100644 index 6b870903f64..00000000000 --- a/.kokoro/release/bump_snapshot.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/release/bump_snapshot.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/release/bump_snapshot.sh b/.kokoro/release/bump_snapshot.sh deleted file mode 100755 index 65dc2b5a412..00000000000 --- a/.kokoro/release/bump_snapshot.sh +++ /dev/null @@ -1,30 +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 snapshot release PR immediately after publishing a release - 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 \ - --snapshot \ - --release-type=java-auth-yoshi -fi diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg deleted file mode 100644 index 7872aea54d8..00000000000 --- a/.kokoro/release/common.cfg +++ /dev/null @@ -1,49 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# 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/java11" -} - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 70247 - keyname: "maven-gpg-keyring" - } - } -} - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 70247 - keyname: "maven-gpg-passphrase" - } - } -} - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 70247 - keyname: "maven-gpg-pubkeyring" - } - } -} - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 70247 - keyname: "sonatype-credentials" - } - } -} diff --git a/.kokoro/release/common.sh b/.kokoro/release/common.sh deleted file mode 100755 index 7f78ee414fa..00000000000 --- a/.kokoro/release/common.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -eo pipefail - -# Get secrets from keystore and set and environment variables -setup_environment_secrets() { - export GPG_PASSPHRASE=$(cat ${KOKORO_KEYSTORE_DIR}/70247_maven-gpg-passphrase) - export GPG_TTY=$(tty) - export GPG_HOMEDIR=/gpg - mkdir $GPG_HOMEDIR - mv ${KOKORO_KEYSTORE_DIR}/70247_maven-gpg-pubkeyring $GPG_HOMEDIR/pubring.gpg - mv ${KOKORO_KEYSTORE_DIR}/70247_maven-gpg-keyring $GPG_HOMEDIR/secring.gpg - export SONATYPE_USERNAME=$(cat ${KOKORO_KEYSTORE_DIR}/70247_sonatype-credentials | cut -f1 -d'|') - export SONATYPE_PASSWORD=$(cat ${KOKORO_KEYSTORE_DIR}/70247_sonatype-credentials | cut -f2 -d'|') -} - -create_settings_xml_file() { - echo " - - - ossrh - ${SONATYPE_USERNAME} - ${SONATYPE_PASSWORD} - - - sonatype-nexus-staging - ${SONATYPE_USERNAME} - ${SONATYPE_PASSWORD} - - - sonatype-nexus-snapshots - ${SONATYPE_USERNAME} - ${SONATYPE_PASSWORD} - - -" > $1 -} \ No newline at end of file diff --git a/.kokoro/release/drop.cfg b/.kokoro/release/drop.cfg deleted file mode 100644 index d541e5ac427..00000000000 --- a/.kokoro/release/drop.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/java-spanner/.kokoro/release/drop.sh" -} diff --git a/.kokoro/release/drop.sh b/.kokoro/release/drop.sh deleted file mode 100755 index 742ec1a8860..00000000000 --- a/.kokoro/release/drop.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -eo pipefail - -# STAGING_REPOSITORY_ID must be set -if [ -z "${STAGING_REPOSITORY_ID}" ]; then - echo "Missing STAGING_REPOSITORY_ID environment variable" - exit 1 -fi - -source $(dirname "$0")/common.sh -pushd $(dirname "$0")/../../ - -setup_environment_secrets -create_settings_xml_file "settings.xml" - -mvn nexus-staging:drop -B \ - --settings=settings.xml \ - -DstagingRepositoryId=${STAGING_REPOSITORY_ID} diff --git a/.kokoro/release/promote.cfg b/.kokoro/release/promote.cfg deleted file mode 100644 index 5551fdd0dd7..00000000000 --- a/.kokoro/release/promote.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/java-spanner/.kokoro/release/promote.sh" -} diff --git a/.kokoro/release/promote.sh b/.kokoro/release/promote.sh deleted file mode 100755 index 3cac3d8a97d..00000000000 --- a/.kokoro/release/promote.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -eo pipefail - -# STAGING_REPOSITORY_ID must be set -if [ -z "${STAGING_REPOSITORY_ID}" ]; then - echo "Missing STAGING_REPOSITORY_ID environment variable" - exit 1 -fi - -source $(dirname "$0")/common.sh - -pushd $(dirname "$0")/../../ - -setup_environment_secrets -create_settings_xml_file "settings.xml" - -mvn nexus-staging:release -B \ - -DperformRelease=true \ - --settings=settings.xml \ - -DstagingRepositoryId=${STAGING_REPOSITORY_ID} diff --git a/.kokoro/release/publish_javadoc.cfg b/.kokoro/release/publish_javadoc.cfg deleted file mode 100644 index 59b87a36b2b..00000000000 --- a/.kokoro/release/publish_javadoc.cfg +++ /dev/null @@ -1,23 +0,0 @@ -# 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: "TRAMPOLINE_BUILD_FILE" - value: "github/java-spanner/.kokoro/release/publish_javadoc.sh" -} - - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "docuploader_service_account" - } - } -} diff --git a/.kokoro/release/publish_javadoc.sh b/.kokoro/release/publish_javadoc.sh deleted file mode 100755 index 2a5ac207734..00000000000 --- a/.kokoro/release/publish_javadoc.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -eo pipefail - -if [[ -z "${CREDENTIALS}" ]]; then - CREDENTIALS=${KOKORO_KEYSTORE_DIR}/73713_docuploader_service_account -fi - -if [[ -z "${STAGING_BUCKET}" ]]; then - echo "Need to set STAGING_BUCKET environment variable" - exit 1 -fi - -# work from the git root directory -pushd $(dirname "$0")/../../ - -# install docuploader package -python3 -m pip install --require-hashes -r .kokoro/requirements.txt - -# compile all packages -mvn clean install -B -q -DskipTests=true - -export NAME=google-cloud-spanner -export VERSION=$(grep ${NAME}: versions.txt | cut -d: -f3) - -# build the docs -mvn site -B -q - -pushd target/site/apidocs - -# 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} diff --git a/.kokoro/release/publish_javadoc11.cfg b/.kokoro/release/publish_javadoc11.cfg deleted file mode 100644 index a432da71657..00000000000 --- a/.kokoro/release/publish_javadoc11.cfg +++ /dev/null @@ -1,30 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# cloud-rad production -env_vars: { - key: "STAGING_BUCKET_V2" - value: "docs-staging-v2" -} - -# Configure the docker image for kokoro-trampoline -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java11" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/java-spanner/.kokoro/release/publish_javadoc11.sh" -} - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "docuploader_service_account" - } - } -} - -# Downloads docfx doclet resource. This will be in ${KOKORO_GFILE_DIR}/ -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/docfx" diff --git a/.kokoro/release/publish_javadoc11.sh b/.kokoro/release/publish_javadoc11.sh deleted file mode 100755 index fa58188fb4e..00000000000 --- a/.kokoro/release/publish_javadoc11.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/bash -# Copyright 2021 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -eo pipefail - -if [[ -z "${CREDENTIALS}" ]]; then - CREDENTIALS=${KOKORO_KEYSTORE_DIR}/73713_docuploader_service_account -fi - -if [[ -z "${STAGING_BUCKET_V2}" ]]; then - echo "Need to set STAGING_BUCKET_V2 environment variable" - exit 1 -fi - -# work from the git root directory -pushd $(dirname "$0")/../../ - -# install docuploader package -python3 -m pip install --require-hashes -r .kokoro/requirements.txt - -# compile all packages -mvn clean install -B -q -DskipTests=true - -export NAME=google-cloud-spanner -export VERSION=$(grep ${NAME}: versions.txt | cut -d: -f3) - -# cloud RAD generation -mvn clean javadoc:aggregate -B -q -P docFX -# include CHANGELOG -cp CHANGELOG.md target/docfx-yml/history.md - -pushd target/docfx-yml - -# create metadata -python3 -m docuploader create-metadata \ - --name ${NAME} \ - --version ${VERSION} \ - --xrefs devsite://java/gax \ - --xrefs devsite://java/google-cloud-core \ - --xrefs devsite://java/api-common \ - --xrefs devsite://java/proto-google-common-protos \ - --xrefs devsite://java/google-api-client \ - --xrefs devsite://java/google-http-client \ - --xrefs devsite://java/protobuf \ - --language java - -# upload yml to production bucket -python3 -m docuploader upload . \ - --credentials ${CREDENTIALS} \ - --staging-bucket ${STAGING_BUCKET_V2} \ - --destination-prefix docfx diff --git a/.kokoro/release/snapshot.cfg b/.kokoro/release/snapshot.cfg deleted file mode 100644 index 1f10492392c..00000000000 --- a/.kokoro/release/snapshot.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/java-spanner/.kokoro/release/snapshot.sh" -} \ No newline at end of file diff --git a/.kokoro/release/snapshot.sh b/.kokoro/release/snapshot.sh deleted file mode 100755 index 1f55b77024d..00000000000 --- a/.kokoro/release/snapshot.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -eo pipefail - -source $(dirname "$0")/common.sh -MAVEN_SETTINGS_FILE=$(realpath $(dirname "$0")/../../)/settings.xml -pushd $(dirname "$0")/../../ - -# ensure we're trying to push a snapshot (no-result returns non-zero exit code) -grep SNAPSHOT versions.txt - -setup_environment_secrets -create_settings_xml_file "settings.xml" - -mvn clean deploy -B \ - --settings ${MAVEN_SETTINGS_FILE} \ - -DperformRelease=true \ - -Dgpg.executable=gpg \ - -Dgpg.passphrase=${GPG_PASSPHRASE} \ - -Dgpg.homedir=${GPG_HOMEDIR} diff --git a/.kokoro/release/stage.cfg b/.kokoro/release/stage.cfg deleted file mode 100644 index 462d561d2d3..00000000000 --- a/.kokoro/release/stage.cfg +++ /dev/null @@ -1,19 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/java-spanner/.kokoro/release/stage.sh" -} - -# Need to save the properties file -action { - define_artifacts { - regex: "github/java-spanner/target/nexus-staging/staging/*.properties" - strip_prefix: "github/java-spanner" - } -} - -env_vars: { - key: "SECRET_MANAGER_KEYS" - value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem" -} diff --git a/.kokoro/release/stage.sh b/.kokoro/release/stage.sh deleted file mode 100755 index 61e714d6ba5..00000000000 --- a/.kokoro/release/stage.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -eo pipefail - -# Start the releasetool reporter -requirementsFile=$(realpath $(dirname "${BASH_SOURCE[0]}")/../requirements.txt) -python3 -m pip install --require-hashes -r $requirementsFile -python3 -m releasetool publish-reporter-script > /tmp/publisher-script; source /tmp/publisher-script - -source $(dirname "$0")/common.sh -source $(dirname "$0")/../common.sh -MAVEN_SETTINGS_FILE=$(realpath $(dirname "$0")/../../)/settings.xml -pushd $(dirname "$0")/../../ - -setup_environment_secrets -create_settings_xml_file "settings.xml" - -# attempt to stage 3 times with exponential backoff (starting with 10 seconds) -retry_with_backoff 3 10 \ - mvn clean deploy -B \ - --settings ${MAVEN_SETTINGS_FILE} \ - -DskipTests=true \ - -Dclirr.skip=true \ - -DperformRelease=true \ - -Dgpg.executable=gpg \ - -Dgpg.passphrase=${GPG_PASSPHRASE} \ - -Dgpg.homedir=${GPG_HOMEDIR} - -if [[ -n "${AUTORELEASE_PR}" ]] -then - mvn nexus-staging:release -B \ - -DperformRelease=true \ - --settings=settings.xml -fi diff --git a/.kokoro/requirements.in b/.kokoro/requirements.in deleted file mode 100644 index 2092cc741d9..00000000000 --- a/.kokoro/requirements.in +++ /dev/null @@ -1,6 +0,0 @@ -gcp-docuploader -gcp-releasetool>=1.10.5 # required for compatibility with cryptography>=39.x -wheel -setuptools -typing-extensions -click<8.1.0 \ No newline at end of file diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt deleted file mode 100644 index 445c5c1f092..00000000000 --- a/.kokoro/requirements.txt +++ /dev/null @@ -1,491 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.9 -# by the following command: -# -# pip-compile requirements.in --generate-hashes --upgrade -# -attrs==23.1.0 \ - --hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \ - --hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015 - # via gcp-releasetool -cachetools==5.3.1 \ - --hash=sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590 \ - --hash=sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b - # via google-auth -certifi==2023.7.22 \ - --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \ - --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9 - # via requests -cffi==1.15.1 \ - --hash=sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5 \ - --hash=sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef \ - --hash=sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104 \ - --hash=sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426 \ - --hash=sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405 \ - --hash=sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375 \ - --hash=sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a \ - --hash=sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e \ - --hash=sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc \ - --hash=sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf \ - --hash=sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185 \ - --hash=sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497 \ - --hash=sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3 \ - --hash=sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35 \ - --hash=sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c \ - --hash=sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83 \ - --hash=sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21 \ - --hash=sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca \ - --hash=sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984 \ - --hash=sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac \ - --hash=sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd \ - --hash=sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee \ - --hash=sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a \ - --hash=sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2 \ - --hash=sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192 \ - --hash=sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7 \ - --hash=sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585 \ - --hash=sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f \ - --hash=sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e \ - --hash=sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27 \ - --hash=sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b \ - --hash=sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e \ - --hash=sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e \ - --hash=sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d \ - --hash=sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c \ - --hash=sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415 \ - --hash=sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82 \ - --hash=sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02 \ - --hash=sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314 \ - --hash=sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325 \ - --hash=sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c \ - --hash=sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3 \ - --hash=sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914 \ - --hash=sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045 \ - --hash=sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d \ - --hash=sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9 \ - --hash=sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5 \ - --hash=sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2 \ - --hash=sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c \ - --hash=sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3 \ - --hash=sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2 \ - --hash=sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8 \ - --hash=sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d \ - --hash=sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d \ - --hash=sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9 \ - --hash=sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162 \ - --hash=sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76 \ - --hash=sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4 \ - --hash=sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e \ - --hash=sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9 \ - --hash=sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6 \ - --hash=sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b \ - --hash=sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01 \ - --hash=sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0 - # via cryptography -charset-normalizer==3.2.0 \ - --hash=sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96 \ - --hash=sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c \ - --hash=sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710 \ - --hash=sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706 \ - --hash=sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020 \ - --hash=sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252 \ - --hash=sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad \ - --hash=sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329 \ - --hash=sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a \ - --hash=sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f \ - --hash=sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6 \ - --hash=sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4 \ - --hash=sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a \ - --hash=sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46 \ - --hash=sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2 \ - --hash=sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23 \ - --hash=sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace \ - --hash=sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd \ - --hash=sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982 \ - --hash=sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10 \ - --hash=sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2 \ - --hash=sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea \ - --hash=sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09 \ - --hash=sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5 \ - --hash=sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149 \ - --hash=sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489 \ - --hash=sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9 \ - --hash=sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80 \ - --hash=sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592 \ - --hash=sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3 \ - --hash=sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6 \ - --hash=sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed \ - --hash=sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c \ - --hash=sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200 \ - --hash=sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a \ - --hash=sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e \ - --hash=sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d \ - --hash=sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6 \ - --hash=sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623 \ - --hash=sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669 \ - --hash=sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3 \ - --hash=sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa \ - --hash=sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9 \ - --hash=sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2 \ - --hash=sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f \ - --hash=sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1 \ - --hash=sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4 \ - --hash=sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a \ - --hash=sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8 \ - --hash=sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3 \ - --hash=sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029 \ - --hash=sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f \ - --hash=sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959 \ - --hash=sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22 \ - --hash=sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7 \ - --hash=sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952 \ - --hash=sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346 \ - --hash=sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e \ - --hash=sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d \ - --hash=sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299 \ - --hash=sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd \ - --hash=sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a \ - --hash=sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3 \ - --hash=sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037 \ - --hash=sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94 \ - --hash=sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c \ - --hash=sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858 \ - --hash=sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a \ - --hash=sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449 \ - --hash=sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c \ - --hash=sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918 \ - --hash=sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1 \ - --hash=sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c \ - --hash=sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac \ - --hash=sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa - # via requests -click==8.0.4 \ - --hash=sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1 \ - --hash=sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb - # via - # -r requirements.in - # gcp-docuploader - # gcp-releasetool -colorlog==6.7.0 \ - --hash=sha256:0d33ca236784a1ba3ff9c532d4964126d8a2c44f1f0cb1d2b0728196f512f662 \ - --hash=sha256:bd94bd21c1e13fac7bd3153f4bc3a7dc0eb0974b8bc2fdf1a989e474f6e582e5 - # via gcp-docuploader -cryptography==41.0.6 \ - --hash=sha256:068bc551698c234742c40049e46840843f3d98ad7ce265fd2bd4ec0d11306596 \ - --hash=sha256:0f27acb55a4e77b9be8d550d762b0513ef3fc658cd3eb15110ebbcbd626db12c \ - --hash=sha256:2132d5865eea673fe6712c2ed5fb4fa49dba10768bb4cc798345748380ee3660 \ - --hash=sha256:3288acccef021e3c3c10d58933f44e8602cf04dba96d9796d70d537bb2f4bbc4 \ - --hash=sha256:35f3f288e83c3f6f10752467c48919a7a94b7d88cc00b0668372a0d2ad4f8ead \ - --hash=sha256:398ae1fc711b5eb78e977daa3cbf47cec20f2c08c5da129b7a296055fbb22aed \ - --hash=sha256:422e3e31d63743855e43e5a6fcc8b4acab860f560f9321b0ee6269cc7ed70cc3 \ - --hash=sha256:48783b7e2bef51224020efb61b42704207dde583d7e371ef8fc2a5fb6c0aabc7 \ - --hash=sha256:4d03186af98b1c01a4eda396b137f29e4e3fb0173e30f885e27acec8823c1b09 \ - --hash=sha256:5daeb18e7886a358064a68dbcaf441c036cbdb7da52ae744e7b9207b04d3908c \ - --hash=sha256:60e746b11b937911dc70d164060d28d273e31853bb359e2b2033c9e93e6f3c43 \ - --hash=sha256:742ae5e9a2310e9dade7932f9576606836ed174da3c7d26bc3d3ab4bd49b9f65 \ - --hash=sha256:7e00fb556bda398b99b0da289ce7053639d33b572847181d6483ad89835115f6 \ - --hash=sha256:85abd057699b98fce40b41737afb234fef05c67e116f6f3650782c10862c43da \ - --hash=sha256:8efb2af8d4ba9dbc9c9dd8f04d19a7abb5b49eab1f3694e7b5a16a5fc2856f5c \ - --hash=sha256:ae236bb8760c1e55b7a39b6d4d32d2279bc6c7c8500b7d5a13b6fb9fc97be35b \ - --hash=sha256:afda76d84b053923c27ede5edc1ed7d53e3c9f475ebaf63c68e69f1403c405a8 \ - --hash=sha256:b27a7fd4229abef715e064269d98a7e2909ebf92eb6912a9603c7e14c181928c \ - --hash=sha256:b648fe2a45e426aaee684ddca2632f62ec4613ef362f4d681a9a6283d10e079d \ - --hash=sha256:c5a550dc7a3b50b116323e3d376241829fd326ac47bc195e04eb33a8170902a9 \ - --hash=sha256:da46e2b5df770070412c46f87bac0849b8d685c5f2679771de277a422c7d0b86 \ - --hash=sha256:f39812f70fc5c71a15aa3c97b2bbe213c3f2a460b79bd21c40d033bb34a9bf36 \ - --hash=sha256:ff369dd19e8fe0528b02e8df9f2aeb2479f89b1270d90f96a63500afe9af5cae - # via - # gcp-releasetool - # secretstorage -gcp-docuploader==0.6.5 \ - --hash=sha256:30221d4ac3e5a2b9c69aa52fdbef68cc3f27d0e6d0d90e220fc024584b8d2318 \ - --hash=sha256:b7458ef93f605b9d46a4bf3a8dc1755dad1f31d030c8679edf304e343b347eea - # via -r requirements.in -gcp-releasetool==1.16.0 \ - --hash=sha256:27bf19d2e87aaa884096ff941aa3c592c482be3d6a2bfe6f06afafa6af2353e3 \ - --hash=sha256:a316b197a543fd036209d0caba7a8eb4d236d8e65381c80cbc6d7efaa7606d63 - # via -r requirements.in -google-api-core==2.11.1 \ - --hash=sha256:25d29e05a0058ed5f19c61c0a78b1b53adea4d9364b464d014fbda941f6d1c9a \ - --hash=sha256:d92a5a92dc36dd4f4b9ee4e55528a90e432b059f93aee6ad857f9de8cc7ae94a - # via - # google-cloud-core - # google-cloud-storage -google-auth==2.22.0 \ - --hash=sha256:164cba9af4e6e4e40c3a4f90a1a6c12ee56f14c0b4868d1ca91b32826ab334ce \ - --hash=sha256:d61d1b40897407b574da67da1a833bdc10d5a11642566e506565d1b1a46ba873 - # via - # gcp-releasetool - # google-api-core - # google-cloud-core - # google-cloud-storage -google-cloud-core==2.3.3 \ - --hash=sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb \ - --hash=sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863 - # via google-cloud-storage -google-cloud-storage==2.10.0 \ - --hash=sha256:934b31ead5f3994e5360f9ff5750982c5b6b11604dc072bc452c25965e076dc7 \ - --hash=sha256:9433cf28801671de1c80434238fb1e7e4a1ba3087470e90f70c928ea77c2b9d7 - # via gcp-docuploader -google-crc32c==1.5.0 \ - --hash=sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a \ - --hash=sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876 \ - --hash=sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c \ - --hash=sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289 \ - --hash=sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298 \ - --hash=sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02 \ - --hash=sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f \ - --hash=sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2 \ - --hash=sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a \ - --hash=sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb \ - --hash=sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210 \ - --hash=sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5 \ - --hash=sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee \ - --hash=sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c \ - --hash=sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a \ - --hash=sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314 \ - --hash=sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd \ - --hash=sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65 \ - --hash=sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37 \ - --hash=sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4 \ - --hash=sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13 \ - --hash=sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894 \ - --hash=sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31 \ - --hash=sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e \ - --hash=sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709 \ - --hash=sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740 \ - --hash=sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc \ - --hash=sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d \ - --hash=sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c \ - --hash=sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c \ - --hash=sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d \ - --hash=sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906 \ - --hash=sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61 \ - --hash=sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57 \ - --hash=sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c \ - --hash=sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a \ - --hash=sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438 \ - --hash=sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946 \ - --hash=sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7 \ - --hash=sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96 \ - --hash=sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091 \ - --hash=sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae \ - --hash=sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d \ - --hash=sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88 \ - --hash=sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2 \ - --hash=sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd \ - --hash=sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541 \ - --hash=sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728 \ - --hash=sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178 \ - --hash=sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968 \ - --hash=sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346 \ - --hash=sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8 \ - --hash=sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93 \ - --hash=sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7 \ - --hash=sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273 \ - --hash=sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462 \ - --hash=sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94 \ - --hash=sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd \ - --hash=sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e \ - --hash=sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57 \ - --hash=sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b \ - --hash=sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9 \ - --hash=sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a \ - --hash=sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100 \ - --hash=sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325 \ - --hash=sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183 \ - --hash=sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556 \ - --hash=sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4 - # via google-resumable-media -google-resumable-media==2.5.0 \ - --hash=sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93 \ - --hash=sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec - # via google-cloud-storage -googleapis-common-protos==1.59.1 \ - --hash=sha256:0cbedb6fb68f1c07e18eb4c48256320777707e7d0c55063ae56c15db3224a61e \ - --hash=sha256:b35d530fe825fb4227857bc47ad84c33c809ac96f312e13182bdeaa2abe1178a - # via google-api-core -idna==3.4 \ - --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ - --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 - # via requests -importlib-metadata==6.8.0 \ - --hash=sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb \ - --hash=sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743 - # via keyring -jaraco-classes==3.3.0 \ - --hash=sha256:10afa92b6743f25c0cf5f37c6bb6e18e2c5bb84a16527ccfc0040ea377e7aaeb \ - --hash=sha256:c063dd08e89217cee02c8d5e5ec560f2c8ce6cdc2fcdc2e68f7b2e5547ed3621 - # via keyring -jeepney==0.8.0 \ - --hash=sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806 \ - --hash=sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755 - # via - # keyring - # secretstorage -jinja2==3.1.2 \ - --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \ - --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61 - # via gcp-releasetool -keyring==24.2.0 \ - --hash=sha256:4901caaf597bfd3bbd78c9a0c7c4c29fcd8310dab2cffefe749e916b6527acd6 \ - --hash=sha256:ca0746a19ec421219f4d713f848fa297a661a8a8c1504867e55bfb5e09091509 - # via gcp-releasetool -markupsafe==2.1.3 \ - --hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \ - --hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \ - --hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \ - --hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \ - --hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \ - --hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \ - --hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \ - --hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \ - --hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \ - --hash=sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9 \ - --hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \ - --hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \ - --hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \ - --hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \ - --hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \ - --hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \ - --hash=sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac \ - --hash=sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52 \ - --hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \ - --hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \ - --hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \ - --hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \ - --hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \ - --hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \ - --hash=sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0 \ - --hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \ - --hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \ - --hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \ - --hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \ - --hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \ - --hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \ - --hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \ - --hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \ - --hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \ - --hash=sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad \ - --hash=sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee \ - --hash=sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc \ - --hash=sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2 \ - --hash=sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48 \ - --hash=sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7 \ - --hash=sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e \ - --hash=sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b \ - --hash=sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa \ - --hash=sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5 \ - --hash=sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e \ - --hash=sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb \ - --hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \ - --hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \ - --hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \ - --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 - # via jinja2 -more-itertools==9.1.0 \ - --hash=sha256:cabaa341ad0389ea83c17a94566a53ae4c9d07349861ecb14dc6d0345cf9ac5d \ - --hash=sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3 - # via jaraco-classes -packaging==23.1 \ - --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \ - --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f - # via gcp-releasetool -protobuf==3.20.3 \ - --hash=sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7 \ - --hash=sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c \ - --hash=sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2 \ - --hash=sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b \ - --hash=sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050 \ - --hash=sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9 \ - --hash=sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7 \ - --hash=sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454 \ - --hash=sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480 \ - --hash=sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469 \ - --hash=sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c \ - --hash=sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e \ - --hash=sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db \ - --hash=sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905 \ - --hash=sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b \ - --hash=sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86 \ - --hash=sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4 \ - --hash=sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402 \ - --hash=sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7 \ - --hash=sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4 \ - --hash=sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99 \ - --hash=sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee - # via - # gcp-docuploader - # gcp-releasetool - # google-api-core - # googleapis-common-protos -pyasn1==0.5.0 \ - --hash=sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57 \ - --hash=sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde - # via - # pyasn1-modules - # rsa -pyasn1-modules==0.3.0 \ - --hash=sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c \ - --hash=sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d - # via google-auth -pycparser==2.21 \ - --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \ - --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206 - # via cffi -pyjwt==2.7.0 \ - --hash=sha256:ba2b425b15ad5ef12f200dc67dd56af4e26de2331f965c5439994dad075876e1 \ - --hash=sha256:bd6ca4a3c4285c1a2d4349e5a035fdf8fb94e04ccd0fcbe6ba289dae9cc3e074 - # via gcp-releasetool -pyperclip==1.8.2 \ - --hash=sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57 - # via gcp-releasetool -python-dateutil==2.8.2 \ - --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ - --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 - # via gcp-releasetool -requests==2.31.0 \ - --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \ - --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1 - # via - # gcp-releasetool - # google-api-core - # google-cloud-storage -rsa==4.9 \ - --hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \ - --hash=sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21 - # via google-auth -secretstorage==3.3.3 \ - --hash=sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77 \ - --hash=sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99 - # via keyring -six==1.16.0 \ - --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ - --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 - # via - # gcp-docuploader - # google-auth - # python-dateutil -typing-extensions==4.7.1 \ - --hash=sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36 \ - --hash=sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2 - # via -r requirements.in -urllib3==1.26.18 \ - --hash=sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07 \ - --hash=sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0 - # via - # google-auth - # requests -wheel==0.40.0 \ - --hash=sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873 \ - --hash=sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247 - # via -r requirements.in -zipp==3.16.1 \ - --hash=sha256:0b37c326d826d5ca35f2b9685cd750292740774ef16190008b00a0227c256fe0 \ - --hash=sha256:857b158da2cbf427b376da1c24fd11faecbac5a4ac7523c3607f8a01f94c2ec0 - # via importlib-metadata - -# WARNING: The following packages were not pinned, but pip requires them to be -# pinned when the requirements file includes hashes and the requirement is not -# satisfied by a package already installed. Consider using the --allow-unsafe flag. -# setuptools diff --git a/CHANGELOG.md b/CHANGELOG.md index e0d281d6f31..e8680d3ccfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## [6.58.0](https://github.com/googleapis/java-spanner/compare/v6.57.0...v6.58.0) (2024-02-08) + + +### Features + +* Open telemetry implementation ([#2770](https://github.com/googleapis/java-spanner/issues/2770)) ([244d6a8](https://github.com/googleapis/java-spanner/commit/244d6a836795bf07dacd6b766436dbd6bf5fa912)) +* **spanner:** Support max_commit_delay in Spanner transactions ([#2854](https://github.com/googleapis/java-spanner/issues/2854)) ([e2b7ae6](https://github.com/googleapis/java-spanner/commit/e2b7ae66648ea775c18c71ab353edd6c0f50e7ac)) +* Support Directed Read in Connection API ([#2855](https://github.com/googleapis/java-spanner/issues/2855)) ([ee477c2](https://github.com/googleapis/java-spanner/commit/ee477c2e7c509ce4b7c43da3b68c1433c59e46fb)) + + +### Bug Fixes + +* Cast for Proto type ([#2862](https://github.com/googleapis/java-spanner/issues/2862)) ([0a95dba](https://github.com/googleapis/java-spanner/commit/0a95dba47681c9c4cc4e41ecfb5dadec6357bff6)) +* Ignore UnsupportedOperationException for virtual threads ([#2866](https://github.com/googleapis/java-spanner/issues/2866)) ([aa9ad7f](https://github.com/googleapis/java-spanner/commit/aa9ad7f5a5e2405e8082a542916c3d1fa7d0fa25)) +* Use default query options with statement cache ([#2860](https://github.com/googleapis/java-spanner/issues/2860)) ([741e4cf](https://github.com/googleapis/java-spanner/commit/741e4cf4eb51c4635078cfe2c52b7462bd4cbbd8)) + + +### Dependencies + +* Update dependency com.google.cloud:google-cloud-shared-dependencies to v3.24.0 ([#2856](https://github.com/googleapis/java-spanner/issues/2856)) ([968877e](https://github.com/googleapis/java-spanner/commit/968877e4eff7da3ff27180c2a6129b04922d1af4)) + ## [6.57.0](https://github.com/googleapis/java-spanner/compare/v6.56.0...v6.57.0) (2024-01-29) diff --git a/README.md b/README.md index 4784fc5caeb..288afeef263 100644 --- a/README.md +++ b/README.md @@ -50,20 +50,20 @@ If you are using Maven without the BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies: ```Groovy -implementation platform('com.google.cloud:libraries-bom:26.30.0') +implementation platform('com.google.cloud:libraries-bom:26.31.0') implementation 'com.google.cloud:google-cloud-spanner' ``` If you are using Gradle without BOM, add this to your dependencies: ```Groovy -implementation 'com.google.cloud:google-cloud-spanner:6.56.0' +implementation 'com.google.cloud:google-cloud-spanner:6.57.0' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.56.0" +libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.57.0" ``` @@ -444,7 +444,7 @@ Java is a registered trademark of Oracle and/or its affiliates. [kokoro-badge-link-5]: http://storage.googleapis.com/cloud-devrel-public/java/badges/java-spanner/java11.html [stability-image]: https://img.shields.io/badge/stability-stable-green [maven-version-image]: https://img.shields.io/maven-central/v/com.google.cloud/google-cloud-spanner.svg -[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.56.0 +[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.57.0 [authentication]: https://github.com/googleapis/google-cloud-java#authentication [auth-scopes]: https://developers.google.com/identity/protocols/oauth2/scopes [predefined-iam-roles]: https://cloud.google.com/iam/docs/understanding-roles#predefined_roles diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index eb873766fa0..2166bc5c6a0 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 - 6.57.0 + 6.58.0 pom com.google.cloud @@ -53,43 +53,43 @@ com.google.cloud google-cloud-spanner - 6.57.0 + 6.58.0 com.google.cloud google-cloud-spanner test-jar - 6.57.0 + 6.58.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.57.0 + 6.58.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.57.0 + 6.58.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.57.0 + 6.58.0 com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.57.0 + 6.58.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.57.0 + 6.58.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.57.0 + 6.58.0 diff --git a/google-cloud-spanner-executor/pom.xml b/google-cloud-spanner-executor/pom.xml index b7cbf4daf65..b0dda360aac 100644 --- a/google-cloud-spanner-executor/pom.xml +++ b/google-cloud-spanner-executor/pom.xml @@ -5,14 +5,14 @@ 4.0.0 com.google.cloud google-cloud-spanner-executor - 6.57.0 + 6.58.0 jar Google Cloud Spanner Executor com.google.cloud google-cloud-spanner-parent - 6.57.0 + 6.58.0 diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index 56e946eb3d4..54eae11d51b 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -426,7 +426,6 @@ com/google/cloud/spanner/connection/Connection void rollbackToSavepoint(java.lang.String) - 7012 @@ -540,4 +539,23 @@ com/google/cloud/spanner/Dialect java.lang.String getDefaultSchema() + + 7005 + com/google/cloud/spanner/PartitionedDmlTransaction + void setSpan(io.opencensus.trace.Span) + void setSpan(com.google.cloud.spanner.ISpan) + + + + + 7012 + com/google/cloud/spanner/connection/Connection + com.google.spanner.v1.DirectedReadOptions getDirectedRead() + + + 7012 + com/google/cloud/spanner/connection/Connection + void setDirectedRead(com.google.spanner.v1.DirectedReadOptions) + + diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index 860a10fc46a..5792068ef23 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 - 6.57.0 + 6.58.0 jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 6.57.0 + 6.58.0 google-cloud-spanner @@ -247,6 +247,14 @@ opencensus-impl test + + io.opentelemetry + opentelemetry-api + + + io.opentelemetry + opentelemetry-context + com.google.auth google-auth-library-oauth2-http @@ -393,7 +401,6 @@ 2.2 test - org.openjdk.jmh @@ -407,9 +414,28 @@ 1.37 test - + + + io.opentelemetry + opentelemetry-sdk + test + + + io.opentelemetry + opentelemetry-sdk-metrics + test + + + io.opentelemetry + opentelemetry-sdk-trace + test + + + io.opentelemetry + opentelemetry-sdk-testing + test + - java9 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 0f4310f9b4d..0714b7651cc 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 @@ -39,6 +39,7 @@ import com.google.cloud.spanner.spi.v1.SpannerRpc; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.ByteString; import com.google.spanner.v1.BeginTransactionRequest; @@ -53,8 +54,6 @@ import com.google.spanner.v1.Transaction; import com.google.spanner.v1.TransactionOptions; import com.google.spanner.v1.TransactionSelector; -import io.opencensus.trace.Span; -import io.opencensus.trace.Tracing; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.Nullable; @@ -70,7 +69,8 @@ abstract class AbstractReadContext abstract static class Builder, T extends AbstractReadContext> { private SessionImpl session; private SpannerRpc rpc; - private Span span = Tracing.getTracer().getCurrentSpan(); + private ISpan span; + private TraceWrapper tracer; private int defaultPrefetchChunks = SpannerOptions.Builder.DEFAULT_PREFETCH_CHUNKS; private QueryOptions defaultQueryOptions = SpannerOptions.Builder.DEFAULT_QUERY_OPTIONS; private DirectedReadOptions defaultDirectedReadOption; @@ -94,11 +94,16 @@ B setRpc(SpannerRpc rpc) { return self(); } - B setSpan(Span span) { + B setSpan(ISpan span) { this.span = span; return self(); } + B setTracer(TraceWrapper tracer) { + this.tracer = tracer; + return self(); + } + B setDefaultPrefetchChunks(int defaultPrefetchChunks) { this.defaultPrefetchChunks = defaultPrefetchChunks; return self(); @@ -389,9 +394,12 @@ void initTransaction() { } transactionId = transaction.getId(); span.addAnnotation( - "Transaction Creation Done", TraceUtil.getTransactionAnnotations(transaction)); + "Transaction Creation Done", + ImmutableMap.of( + "Id", transaction.getId().toStringUtf8(), "Timestamp", timestamp.toString())); + } catch (SpannerException e) { - span.addAnnotation("Transaction Creation Failed", TraceUtil.getExceptionAnnotations(e)); + span.addAnnotation("Transaction Creation Failed", e); throw e; } } @@ -402,7 +410,8 @@ void initTransaction() { final SessionImpl session; final SpannerRpc rpc; final ExecutorProvider executorProvider; - Span span; + ISpan span; + TraceWrapper tracer; private final int defaultPrefetchChunks; private final QueryOptions defaultQueryOptions; @@ -435,10 +444,11 @@ void initTransaction() { this.span = builder.span; this.executorProvider = builder.executorProvider; this.clock = builder.clock; + this.tracer = builder.tracer; } @Override - public void setSpan(Span span) { + public void setSpan(ISpan span) { this.span = span; } @@ -692,6 +702,7 @@ ResultSet executeQueryInternalWithOptions( MAX_BUFFERED_CHUNKS, SpannerImpl.QUERY, span, + tracer, rpc.getExecuteQueryRetrySettings(), rpc.getExecuteQueryRetryableCodes()) { @Override @@ -752,7 +763,7 @@ public final void invalidate() { @Override public void close() { - span.end(TraceUtil.END_SPAN_OPTIONS); + span.end(); synchronized (lock) { isClosed = true; } @@ -837,6 +848,7 @@ ResultSet readInternalWithOptions( MAX_BUFFERED_CHUNKS, SpannerImpl.READ, span, + tracer, rpc.getReadRetrySettings(), rpc.getReadRetryableCodes()) { @Override 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 c18e64165bc..4904a382f92 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 @@ -37,7 +37,6 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.AbstractIterator; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.io.CharSource; import com.google.common.util.concurrent.Uninterruptibles; @@ -53,11 +52,6 @@ import com.google.spanner.v1.Transaction; import com.google.spanner.v1.TypeCode; import io.grpc.Context; -import io.opencensus.common.Scope; -import io.opencensus.trace.AttributeValue; -import io.opencensus.trace.Span; -import io.opencensus.trace.Tracer; -import io.opencensus.trace.Tracing; import java.io.IOException; import java.io.Serializable; import java.math.BigDecimal; @@ -87,7 +81,6 @@ /** Implementation of {@link ResultSet}. */ abstract class AbstractResultSet extends AbstractStructReader implements ResultSet { - private static final Tracer tracer = Tracing.getTracer(); private static final com.google.protobuf.Value NULL_VALUE = com.google.protobuf.Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(); @@ -485,7 +478,10 @@ private Object writeReplace() { case PROTO: builder .set(fieldName) - .to(Value.protoMessage((ByteArray) value, fieldType.getProtoTypeFqn())); + .to( + Value.protoMessage( + value == null ? null : ((LazyByteArray) value).getByteArray(), + fieldType.getProtoTypeFqn())); break; case ENUM: builder.set(fieldName).to(Value.protoEnum((Long) value, fieldType.getProtoTypeFqn())); @@ -817,7 +813,8 @@ protected Value getValueInternal(int columnIndex) { case INT64: return Value.int64(isNull ? null : getLongInternal(columnIndex)); case ENUM: - return Value.protoEnum(getLongInternal(columnIndex), columnType.getProtoTypeFqn()); + return Value.protoEnum( + isNull ? null : getLongInternal(columnIndex), columnType.getProtoTypeFqn()); case NUMERIC: return Value.numeric(isNull ? null : getBigDecimalInternal(columnIndex)); case PG_NUMERIC: @@ -833,7 +830,8 @@ protected Value getValueInternal(int columnIndex) { case BYTES: return Value.internalBytes(isNull ? null : getLazyBytesInternal(columnIndex)); case PROTO: - return Value.protoMessage(getBytesInternal(columnIndex), columnType.getProtoTypeFqn()); + return Value.protoMessage( + isNull ? null : getBytesInternal(columnIndex), columnType.getProtoTypeFqn()); case TIMESTAMP: return Value.timestamp(isNull ? null : getTimestampInternal(columnIndex)); case DATE: @@ -1206,7 +1204,8 @@ abstract static class ResumableStreamIterator extends AbstractIterator buffer = new LinkedList<>(); private final int maxBufferSize; - private final Span span; + private final ISpan span; + private final TraceWrapper tracer; private CloseableIterator stream; private ByteString resumeToken; private boolean finished; @@ -1220,12 +1219,14 @@ abstract static class ResumableStreamIterator extends AbstractIterator retryableCodes) { checkArgument(maxBufferSize >= 0); this.maxBufferSize = maxBufferSize; - this.span = tracer.spanBuilderWithExplicitParent(streamName, parent).startSpan(); + this.tracer = tracer; + this.span = tracer.spanBuilderWithExplicitParent(streamName, parent); this.streamingRetrySettings = Preconditions.checkNotNull(streamingRetrySettings); this.retryableCodes = Preconditions.checkNotNull(retryableCodes); this.backOff = newBackOff(); @@ -1281,11 +1282,7 @@ private static long nextBackOffMillis(BackOff backoff) throws SpannerException { } private void backoffSleep(Context context, long backoffMillis) throws SpannerException { - tracer - .getCurrentSpan() - .addAnnotation( - "Backing off", - ImmutableMap.of("Delay", AttributeValue.longAttributeValue(backoffMillis))); + tracer.getCurrentSpan().addAnnotation("Backing off", "Delay", backoffMillis); final CountDownLatch latch = new CountDownLatch(1); final Context.CancellationListener listener = ignored -> { @@ -1325,7 +1322,7 @@ public void execute(Runnable command) { public void close(@Nullable String message) { if (stream != null) { stream.close(message); - span.end(TraceUtil.END_SPAN_OPTIONS); + span.end(); stream = null; } } @@ -1343,11 +1340,9 @@ protected PartialResultSet computeNext() { if (stream == null) { span.addAnnotation( "Starting/Resuming stream", - ImmutableMap.of( - "ResumeToken", - AttributeValue.stringAttributeValue( - resumeToken == null ? "null" : resumeToken.toStringUtf8()))); - try (Scope s = tracer.withSpan(span)) { + "ResumeToken", + resumeToken == null ? "null" : resumeToken.toStringUtf8()); + try (IScope scope = tracer.withSpan(span)) { // When start a new stream set the Span as current to make the gRPC Span a child of // this Span. stream = checkNotNull(startStream(resumeToken)); @@ -1387,9 +1382,7 @@ protected PartialResultSet computeNext() { } } catch (SpannerException spannerException) { if (safeToRetry && isRetryable(spannerException)) { - span.addAnnotation( - "Stream broken. Safe to retry", - TraceUtil.getExceptionAnnotations(spannerException)); + span.addAnnotation("Stream broken. Safe to retry", spannerException); logger.log(Level.FINE, "Retryable exception, will sleep and retry", spannerException); // Truncate any items in the buffer before the last retry token. while (!buffer.isEmpty() && buffer.getLast().getResumeToken().isEmpty()) { @@ -1397,7 +1390,7 @@ protected PartialResultSet computeNext() { } assert buffer.isEmpty() || buffer.getLast().getResumeToken().equals(resumeToken); stream = null; - try (Scope s = tracer.withSpan(span)) { + try (IScope s = tracer.withSpan(span)) { long delay = spannerException.getRetryDelayInMillis(); if (delay != -1) { backoffSleep(context, delay); @@ -1408,12 +1401,12 @@ protected PartialResultSet computeNext() { continue; } - span.addAnnotation("Stream broken. Not safe to retry"); - TraceUtil.setWithFailure(span, spannerException); + span.addAnnotation("Stream broken. Not safe to retry", spannerException); + span.setStatus(spannerException); throw spannerException; } catch (RuntimeException e) { - span.addAnnotation("Stream broken. Not safe to retry"); - TraceUtil.setWithFailure(span, e); + span.addAnnotation("Stream broken. Not safe to retry", e); + span.setStatus(e); throw e; } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java index 53c49a5a54b..ef6f63d52ea 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java @@ -340,14 +340,16 @@ public List getBooleanList(String columnName) { @Override public long[] getLongArray(int columnIndex) { - checkNonNullOfType(columnIndex, Type.array(Type.int64()), columnIndex); + checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnIndex); + checkArrayElementType(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnIndex); return getLongArrayInternal(columnIndex); } @Override public long[] getLongArray(String columnName) { int columnIndex = getColumnIndex(columnName); - checkNonNullOfType(columnIndex, Type.array(Type.int64()), columnName); + checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnName); + checkArrayElementType(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnName); return getLongArrayInternal(columnIndex); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java index 510bd02bc0e..8b20dd824a0 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java @@ -28,31 +28,27 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.MoreExecutors; -import io.opencensus.trace.Span; -import io.opencensus.trace.Tracer; -import io.opencensus.trace.Tracing; /** Implementation of {@link AsyncTransactionManager}. */ final class AsyncTransactionManagerImpl implements CommittableAsyncTransactionManager, SessionTransaction { - private static final Tracer tracer = Tracing.getTracer(); private final SessionImpl session; - private Span span; + private ISpan span; private final Options options; private TransactionRunnerImpl.TransactionContextImpl txn; private TransactionState txnState; private final SettableApiFuture commitResponse = SettableApiFuture.create(); - AsyncTransactionManagerImpl(SessionImpl session, Span span, TransactionOption... options) { + AsyncTransactionManagerImpl(SessionImpl session, ISpan span, TransactionOption... options) { this.session = session; this.span = span; this.options = Options.fromTransactionOptions(options); } @Override - public void setSpan(Span span) { + public void setSpan(ISpan span) { this.span = span; } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClientImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClientImpl.java index eab90a266c9..664cde1edbb 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClientImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClientImpl.java @@ -30,7 +30,6 @@ import com.google.spanner.v1.PartitionReadRequest; import com.google.spanner.v1.PartitionResponse; import com.google.spanner.v1.TransactionSelector; -import io.opencensus.trace.Tracing; import java.util.List; import java.util.Map; import javax.annotation.Nullable; @@ -62,7 +61,9 @@ public BatchReadOnlyTransaction batchReadOnlyTransaction(TimestampBound bound) { .setExecutorProvider(sessionClient.getSpanner().getAsyncExecutorProvider()) .setDefaultPrefetchChunks(sessionClient.getSpanner().getDefaultPrefetchChunks()) .setDefaultDirectedReadOptions( - sessionClient.getSpanner().getOptions().getDirectedReadOptions()), + sessionClient.getSpanner().getOptions().getDirectedReadOptions()) + .setSpan(sessionClient.getSpanner().getTracer().getCurrentSpan()) + .setTracer(sessionClient.getSpanner().getTracer()), checkNotNull(bound)); } @@ -81,7 +82,9 @@ public BatchReadOnlyTransaction batchReadOnlyTransaction(BatchTransactionId batc .setExecutorProvider(sessionClient.getSpanner().getAsyncExecutorProvider()) .setDefaultPrefetchChunks(sessionClient.getSpanner().getDefaultPrefetchChunks()) .setDefaultDirectedReadOptions( - sessionClient.getSpanner().getOptions().getDirectedReadOptions()), + sessionClient.getSpanner().getOptions().getDirectedReadOptions()) + .setSpan(sessionClient.getSpanner().getTracer().getCurrentSpan()) + .setTracer(sessionClient.getSpanner().getTracer()), batchTransactionId); } @@ -95,7 +98,6 @@ private static class BatchReadOnlyTransactionImpl extends MultiUseReadOnlyTransa super(builder.setTimestampBound(bound)); this.sessionName = session.getName(); this.options = session.getOptions(); - setSpan(Tracing.getTracer().getCurrentSpan()); initTransaction(); } @@ -104,7 +106,6 @@ private static class BatchReadOnlyTransactionImpl extends MultiUseReadOnlyTransa super(builder.setTransactionId(batchTransactionId.getTransactionId())); this.sessionName = session.getName(); this.options = session.getOptions(); - setSpan(Tracing.getTracer().getCurrentSpan()); } @Override diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java index 3835cb1f338..b63ad379305 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java @@ -26,29 +26,25 @@ import com.google.common.base.Function; import com.google.common.util.concurrent.ListenableFuture; import com.google.spanner.v1.BatchWriteResponse; -import io.opencensus.common.Scope; -import io.opencensus.trace.Span; -import io.opencensus.trace.Tracer; -import io.opencensus.trace.Tracing; import javax.annotation.Nullable; class DatabaseClientImpl implements DatabaseClient { private static final String READ_WRITE_TRANSACTION = "CloudSpanner.ReadWriteTransaction"; private static final String READ_ONLY_TRANSACTION = "CloudSpanner.ReadOnlyTransaction"; private static final String PARTITION_DML_TRANSACTION = "CloudSpanner.PartitionDMLTransaction"; - private static final Tracer tracer = Tracing.getTracer(); - + private final TraceWrapper tracer; @VisibleForTesting final String clientId; @VisibleForTesting final SessionPool pool; @VisibleForTesting - DatabaseClientImpl(SessionPool pool) { - this("", pool); + DatabaseClientImpl(SessionPool pool, TraceWrapper tracer) { + this("", pool, tracer); } - DatabaseClientImpl(String clientId, SessionPool pool) { + DatabaseClientImpl(String clientId, SessionPool pool, TraceWrapper tracer) { this.clientId = clientId; this.pool = pool; + this.tracer = tracer; } @VisibleForTesting @@ -76,14 +72,14 @@ public Timestamp write(final Iterable mutations) throws SpannerExcepti public CommitResponse writeWithOptions( final Iterable mutations, final TransactionOption... options) throws SpannerException { - Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(READ_WRITE_TRANSACTION); + try (IScope s = tracer.withSpan(span)) { return runWithSessionRetry(session -> session.writeWithOptions(mutations, options)); } catch (RuntimeException e) { - TraceUtil.setWithFailure(span, e); + span.setStatus(e); throw e; } finally { - span.end(TraceUtil.END_SPAN_OPTIONS); + span.end(); } } @@ -96,15 +92,15 @@ public Timestamp writeAtLeastOnce(final Iterable mutations) throws Spa public CommitResponse writeAtLeastOnceWithOptions( final Iterable mutations, final TransactionOption... options) throws SpannerException { - Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(READ_WRITE_TRANSACTION); + try (IScope s = tracer.withSpan(span)) { return runWithSessionRetry( session -> session.writeAtLeastOnceWithOptions(mutations, options)); } catch (RuntimeException e) { - TraceUtil.setWithFailure(span, e); + span.setStatus(e); throw e; } finally { - span.end(TraceUtil.END_SPAN_OPTIONS); + span.end(); } } @@ -112,136 +108,145 @@ public CommitResponse writeAtLeastOnceWithOptions( public ServerStream batchWriteAtLeastOnce( final Iterable mutationGroups, final TransactionOption... options) throws SpannerException { - Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(READ_WRITE_TRANSACTION); + try (IScope s = tracer.withSpan(span)) { return runWithSessionRetry(session -> session.batchWriteAtLeastOnce(mutationGroups, options)); } catch (RuntimeException e) { - TraceUtil.setWithFailure(span, e); + span.setStatus(e); throw e; } finally { - span.end(TraceUtil.END_SPAN_OPTIONS); + span.end(); } } @Override public ReadContext singleUse() { - Span span = tracer.spanBuilder(READ_ONLY_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(READ_ONLY_TRANSACTION); + try (IScope s = tracer.withSpan(span)) { return getSession().singleUse(); } catch (RuntimeException e) { - TraceUtil.endSpanWithFailure(span, e); + span.setStatus(e); + span.end(); throw e; } } @Override public ReadContext singleUse(TimestampBound bound) { - Span span = tracer.spanBuilder(READ_ONLY_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(READ_ONLY_TRANSACTION); + try (IScope s = tracer.withSpan(span)) { return getSession().singleUse(bound); } catch (RuntimeException e) { - TraceUtil.endSpanWithFailure(span, e); + span.setStatus(e); + span.end(); throw e; } } @Override public ReadOnlyTransaction singleUseReadOnlyTransaction() { - Span span = tracer.spanBuilder(READ_ONLY_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(READ_ONLY_TRANSACTION); + try (IScope s = tracer.withSpan(span)) { return getSession().singleUseReadOnlyTransaction(); } catch (RuntimeException e) { - TraceUtil.endSpanWithFailure(span, e); + span.setStatus(e); + span.end(); throw e; } } @Override public ReadOnlyTransaction singleUseReadOnlyTransaction(TimestampBound bound) { - Span span = tracer.spanBuilder(READ_ONLY_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(READ_ONLY_TRANSACTION); + try (IScope s = tracer.withSpan(span)) { return getSession().singleUseReadOnlyTransaction(bound); } catch (RuntimeException e) { - TraceUtil.endSpanWithFailure(span, e); + span.setStatus(e); + span.end(); throw e; } } @Override public ReadOnlyTransaction readOnlyTransaction() { - Span span = tracer.spanBuilder(READ_ONLY_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(READ_ONLY_TRANSACTION); + try (IScope s = tracer.withSpan(span)) { return getSession().readOnlyTransaction(); } catch (RuntimeException e) { - TraceUtil.endSpanWithFailure(span, e); + span.setStatus(e); + span.end(); throw e; } } @Override public ReadOnlyTransaction readOnlyTransaction(TimestampBound bound) { - Span span = tracer.spanBuilder(READ_ONLY_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(READ_ONLY_TRANSACTION); + try (IScope s = tracer.withSpan(span)) { return getSession().readOnlyTransaction(bound); } catch (RuntimeException e) { - TraceUtil.endSpanWithFailure(span, e); + span.setStatus(e); + span.end(); throw e; } } @Override public TransactionRunner readWriteTransaction(TransactionOption... options) { - Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(READ_WRITE_TRANSACTION); + try (IScope s = tracer.withSpan(span)) { return getSession().readWriteTransaction(options); } catch (RuntimeException e) { - TraceUtil.endSpanWithFailure(span, e); + span.setStatus(e); + span.end(); throw e; - } finally { - span.end(TraceUtil.END_SPAN_OPTIONS); } } @Override public TransactionManager transactionManager(TransactionOption... options) { - Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(READ_WRITE_TRANSACTION); + try (IScope s = tracer.withSpan(span)) { return getSession().transactionManager(options); } catch (RuntimeException e) { - TraceUtil.endSpanWithFailure(span, e); + span.setStatus(e); + span.end(); throw e; } } @Override public AsyncRunner runAsync(TransactionOption... options) { - Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(READ_WRITE_TRANSACTION); + try (IScope s = tracer.withSpan(span)) { return getSession().runAsync(options); } catch (RuntimeException e) { - TraceUtil.endSpanWithFailure(span, e); + span.setStatus(e); + span.end(); throw e; } } @Override public AsyncTransactionManager transactionManagerAsync(TransactionOption... options) { - Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(READ_WRITE_TRANSACTION); + try (IScope s = tracer.withSpan(span)) { return getSession().transactionManagerAsync(options); } catch (RuntimeException e) { - TraceUtil.endSpanWithFailure(span, e); + span.setStatus(e); + span.end(); throw e; } } @Override public long executePartitionedUpdate(final Statement stmt, final UpdateOption... options) { - Span span = tracer.spanBuilder(PARTITION_DML_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(PARTITION_DML_TRANSACTION); + try (IScope s = tracer.withSpan(span)) { return runWithSessionRetry(session -> session.executePartitionedUpdate(stmt, options)); } catch (RuntimeException e) { - TraceUtil.endSpanWithFailure(span, e); + span.setStatus(e); + span.end(); throw e; } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/IScope.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/IScope.java new file mode 100644 index 00000000000..cbefe47b887 --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/IScope.java @@ -0,0 +1,26 @@ +/* + * Copyright 2023 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; + +/** + * This interface represents a scope that wraps both OpenCensus and OpenTelemetry scopes. It extends + * the AutoCloseable interface and overrides the close method that does not throw an exception. + */ +interface IScope extends AutoCloseable { + @Override + void close(); +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ISpan.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ISpan.java new file mode 100644 index 00000000000..ce837de0e58 --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ISpan.java @@ -0,0 +1,45 @@ +/* + * Copyright 2023 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 java.util.Map; + +interface ISpan { + + /** + * Adds an annotation to the OpenCensus and OpenTelemetry span. + * + * @param message the description of the annotation event. + * @param attributes the map of attribute key-value pairs that will be added; these are associated + * with this annotation. + */ + void addAnnotation(String message, Map attributes); + + void addAnnotation(String message); + + void addAnnotation(String message, String key, String value); + + void addAnnotation(String message, String key, long value); + + void addAnnotation(String message, Throwable e); + + void setStatus(Throwable e); + + void setStatus(ErrorCode errorCode); + + void end(); +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MetricRegistryConstants.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MetricRegistryConstants.java index 3512a75732d..d0098e961e6 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MetricRegistryConstants.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MetricRegistryConstants.java @@ -65,13 +65,20 @@ class MetricRegistryConstants { /** Unit to represent counts. */ static final String COUNT = "1"; + static final String INSTRUMENTATION_SCOPE = "cloud.google.com/java"; + + static final String METRIC_PREFIX = "cloud.google.com/java/"; + // The Metric name and description - static final String MAX_IN_USE_SESSIONS = "cloud.google.com/java/spanner/max_in_use_sessions"; - static final String MAX_ALLOWED_SESSIONS = "cloud.google.com/java/spanner/max_allowed_sessions"; - static final String GET_SESSION_TIMEOUTS = "cloud.google.com/java/spanner/get_session_timeouts"; - static final String NUM_ACQUIRED_SESSIONS = "cloud.google.com/java/spanner/num_acquired_sessions"; - static final String NUM_RELEASED_SESSIONS = "cloud.google.com/java/spanner/num_released_sessions"; - static final String NUM_SESSIONS_IN_POOL = "cloud.google.com/java/spanner/num_sessions_in_pool"; + static final String MAX_IN_USE_SESSIONS = "spanner/max_in_use_sessions"; + static final String MAX_ALLOWED_SESSIONS = "spanner/max_allowed_sessions"; + static final String GET_SESSION_TIMEOUTS = "spanner/get_session_timeouts"; + static final String NUM_ACQUIRED_SESSIONS = "spanner/num_acquired_sessions"; + static final String NUM_RELEASED_SESSIONS = "spanner/num_released_sessions"; + static final String NUM_SESSIONS_IN_POOL = "spanner/num_sessions_in_pool"; + static final String NUM_SESSIONS_IN_USE = "spanner/num_in_use_sessions"; + static final String NUM_SESSIONS_AVAILABLE = "spanner/num_available_sessions"; + static final String SESSIONS_TYPE = "session_type"; static final String MAX_IN_USE_SESSIONS_DESCRIPTION = "The maximum number of sessions in use during the last 10 minute interval."; @@ -84,4 +91,11 @@ class MetricRegistryConstants { static final String NUM_RELEASED_SESSIONS_DESCRIPTION = "The number of sessions released by the user and pool maintainer."; static final String NUM_SESSIONS_IN_POOL_DESCRIPTION = "The number of sessions in the pool."; + + static final String SPANNER_GFE_LATENCY = "spanner/gfe_latency"; + static final String SPANNER_GFE_LATENCY_DESCRIPTION = + "Latency between Google's network receiving an RPC and reading back the first byte of the response"; + static final String SPANNER_GFE_HEADER_MISSING_COUNT = "spanner/gfe_header_missing_count"; + static final String SPANNER_GFE_HEADER_MISSING_COUNT_DESCRIPTION = + "Number of RPC responses received without the server-timing header, most likely means that the RPC never reached Google's network"; } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/OpenCensusScope.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/OpenCensusScope.java new file mode 100644 index 00000000000..81c1db16571 --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/OpenCensusScope.java @@ -0,0 +1,33 @@ +/* + * Copyright 2023 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 io.opencensus.common.Scope; + +class OpenCensusScope implements IScope { + + private final Scope openCensusScope; + + OpenCensusScope(Scope openCensusScope) { + this.openCensusScope = openCensusScope; + } + + @Override + public void close() { + openCensusScope.close(); + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/OpenCensusSpan.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/OpenCensusSpan.java new file mode 100644 index 00000000000..86e43778f31 --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/OpenCensusSpan.java @@ -0,0 +1,111 @@ +/* + * Copyright 2023 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.collect.ImmutableMap; +import io.opencensus.contrib.grpc.util.StatusConverter; +import io.opencensus.trace.AttributeValue; +import io.opencensus.trace.EndSpanOptions; +import io.opencensus.trace.Span; +import io.opencensus.trace.Status; +import java.util.HashMap; +import java.util.Map; + +public class OpenCensusSpan implements ISpan { + + static final EndSpanOptions END_SPAN_OPTIONS = + EndSpanOptions.builder().setSampleToLocalSpanStore(true).build(); + private final Span openCensusSpan; + + public OpenCensusSpan(Span openCensusSpan) { + this.openCensusSpan = openCensusSpan; + } + + Span getOpenCensusSpan() { + return openCensusSpan; + } + + private ImmutableMap getOpenCensusExceptionAnnotations(Throwable e) { + if (e instanceof SpannerException) { + return ImmutableMap.of( + "Status", + AttributeValue.stringAttributeValue(((SpannerException) e).getErrorCode().toString())); + } + return ImmutableMap.of(); + } + + @Override + public void addAnnotation(String message, Map attributes) { + Map ocAttributeValues = new HashMap<>(); + for (Map.Entry entry : attributes.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof String) { + ocAttributeValues.put(key, AttributeValue.stringAttributeValue((String) value)); + } else if (value instanceof Long) { + ocAttributeValues.put(key, AttributeValue.longAttributeValue((Long) value)); + } + } + + if (ocAttributeValues.size() > 0) { + openCensusSpan.addAnnotation(message, ocAttributeValues); + } + } + + @Override + public void addAnnotation(String message) { + openCensusSpan.addAnnotation(message); + } + + @Override + public void addAnnotation(String message, String key, String value) { + openCensusSpan.addAnnotation( + message, ImmutableMap.of(key, AttributeValue.stringAttributeValue(value))); + } + + @Override + public void addAnnotation(String message, String key, long value) { + openCensusSpan.addAnnotation( + message, ImmutableMap.of(key, AttributeValue.longAttributeValue(value))); + } + + @Override + public void addAnnotation(String message, Throwable e) { + openCensusSpan.addAnnotation(message, this.getOpenCensusExceptionAnnotations(e)); + } + + @Override + public void setStatus(Throwable e) { + if (e instanceof SpannerException) { + openCensusSpan.setStatus( + StatusConverter.fromGrpcStatus(((SpannerException) e).getErrorCode().getGrpcStatus()) + .withDescription(e.getMessage())); + } else { + openCensusSpan.setStatus(Status.INTERNAL.withDescription(e.getMessage())); + } + } + + @Override + public void setStatus(ErrorCode errorCode) { + openCensusSpan.setStatus(StatusConverter.fromGrpcStatus(errorCode.getGrpcStatus())); + } + + @Override + public void end() { + openCensusSpan.end(END_SPAN_OPTIONS); + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/OpenTelemetryScope.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/OpenTelemetryScope.java new file mode 100644 index 00000000000..6766bc54e6b --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/OpenTelemetryScope.java @@ -0,0 +1,33 @@ +/* + * Copyright 2023 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 io.opentelemetry.context.Scope; + +class OpenTelemetryScope implements IScope { + + private final Scope openTelemetryScope; + + OpenTelemetryScope(Scope openTelemetryScope) { + this.openTelemetryScope = openTelemetryScope; + } + + @Override + public void close() { + openTelemetryScope.close(); + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/OpenTelemetrySpan.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/OpenTelemetrySpan.java new file mode 100644 index 00000000000..98e1540e760 --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/OpenTelemetrySpan.java @@ -0,0 +1,99 @@ +/* + * Copyright 2023 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 io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; +import java.util.Map; + +class OpenTelemetrySpan implements ISpan { + + private final io.opentelemetry.api.trace.Span openTelemetrySpan; + + OpenTelemetrySpan(Span openTelemetrySpan) { + this.openTelemetrySpan = openTelemetrySpan; + } + + Span getOpenTelemetrySpan() { + return openTelemetrySpan; + } + + @Override + public void addAnnotation(String message, Map attributes) { + AttributesBuilder otAttributesBuilder = Attributes.builder(); + for (Map.Entry entry : attributes.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof String) { + otAttributesBuilder.put(key, (String) value); + } else if (value instanceof Long) { + otAttributesBuilder.put(key, (Long) value); + } + } + openTelemetrySpan.addEvent(message, otAttributesBuilder.build()); + } + + @Override + public void addAnnotation(String message) { + openTelemetrySpan.addEvent(message); + } + + @Override + public void addAnnotation(String message, String key, String value) { + openTelemetrySpan.addEvent(message, Attributes.builder().put(key, value).build()); + } + + @Override + public void addAnnotation(String message, String key, long value) { + openTelemetrySpan.addEvent(message, Attributes.builder().put(key, value).build()); + } + + @Override + public void addAnnotation(String message, Throwable e) { + openTelemetrySpan.addEvent(message, this.createOpenTelemetryExceptionAnnotations(e)); + } + + @Override + public void setStatus(Throwable e) { + if (e instanceof SpannerException) { + openTelemetrySpan.setStatus(StatusCode.ERROR, ((SpannerException) e).getErrorCode().name()); + } else { + openTelemetrySpan.setStatus(StatusCode.ERROR, ErrorCode.INTERNAL.name()); + } + openTelemetrySpan.recordException(e); + } + + @Override + public void setStatus(ErrorCode errorCode) { + openTelemetrySpan.setStatus(StatusCode.ERROR, errorCode.name()); + } + + @Override + public void end() { + openTelemetrySpan.end(); + } + + private Attributes createOpenTelemetryExceptionAnnotations(Throwable e) { + AttributesBuilder attributesBuilder = Attributes.builder(); + if (e instanceof SpannerException) { + attributesBuilder.put("Status", ((SpannerException) e).getErrorCode().toString()); + } + return attributesBuilder.build(); + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java index dda12b60d64..57feabbfcca 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java @@ -20,6 +20,7 @@ import com.google.spanner.v1.DirectedReadOptions; import com.google.spanner.v1.RequestOptions.Priority; import java.io.Serializable; +import java.time.Duration; import java.util.Objects; /** Specifies options for various spanner operations */ @@ -140,6 +141,11 @@ public static ReadQueryUpdateTransactionOption priority(RpcPriority priority) { return new PriorityOption(priority); } + public static ReadQueryUpdateTransactionOption maxCommitDelay(Duration maxCommitDelay) { + Preconditions.checkArgument(!maxCommitDelay.isNegative(), "maxCommitDelay should be positive"); + return new MaxCommitDelayOption(maxCommitDelay); + } + /** * Specifying this will cause the reads, queries, updates and writes operations statistics * collection to be grouped by tag. @@ -247,6 +253,21 @@ void appendToOptions(Options options) { static final CommitStatsOption COMMIT_STATS_OPTION = new CommitStatsOption(); + /** Option to request {@link MaxCommitDelayOption} for read/write transactions. */ + static final class MaxCommitDelayOption extends InternalOption + implements ReadQueryUpdateTransactionOption { + final Duration maxCommitDelay; + + MaxCommitDelayOption(Duration maxCommitDelay) { + this.maxCommitDelay = maxCommitDelay; + } + + @Override + void appendToOptions(Options options) { + options.maxCommitDelay = maxCommitDelay; + } + } + /** Option to request Optimistic Concurrency Control for read/write transactions. */ static final class OptimisticLockOption extends InternalOption implements TransactionOption { @Override @@ -354,6 +375,9 @@ void appendToOptions(Options options) { } private boolean withCommitStats; + + private Duration maxCommitDelay; + private Long limit; private Integer prefetchChunks; private Integer bufferRows; @@ -375,6 +399,14 @@ boolean withCommitStats() { return withCommitStats; } + boolean hasMaxCommitDelay() { + return maxCommitDelay != null; + } + + Duration maxCommitDelay() { + return maxCommitDelay; + } + boolean hasLimit() { return limit != null; } @@ -481,6 +513,9 @@ public String toString() { if (withCommitStats) { b.append("withCommitStats: ").append(withCommitStats).append(' '); } + if (maxCommitDelay != null) { + b.append("maxCommitDelay: ").append(maxCommitDelay).append(' '); + } if (limit != null) { b.append("limit: ").append(limit).append(' '); } @@ -533,6 +568,7 @@ public boolean equals(Object o) { Options that = (Options) o; return Objects.equals(withCommitStats, that.withCommitStats) + && Objects.equals(maxCommitDelay, that.maxCommitDelay) && (!hasLimit() && !that.hasLimit() || hasLimit() && that.hasLimit() && Objects.equals(limit(), that.limit())) && (!hasPrefetchChunks() && !that.hasPrefetchChunks() @@ -562,6 +598,9 @@ public int hashCode() { if (withCommitStats) { result = 31 * result + 1231; } + if (maxCommitDelay != null) { + result = 31 * result + maxCommitDelay.hashCode(); + } if (limit != null) { result = 31 * result + limit.hashCode(); } 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 index 36991b18c3d..cabc270566c 100644 --- 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 @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkState; +import com.google.api.core.InternalApi; import com.google.api.gax.grpc.GrpcStatusCode; import com.google.api.gax.rpc.AbortedException; import com.google.api.gax.rpc.DeadlineExceededException; @@ -38,7 +39,6 @@ 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; @@ -46,6 +46,7 @@ import org.threeten.bp.Duration; import org.threeten.bp.temporal.ChronoUnit; +@InternalApi public class PartitionedDmlTransaction implements SessionImpl.SessionTransaction { private static final Logger LOGGER = Logger.getLogger(PartitionedDmlTransaction.class.getName()); @@ -137,9 +138,9 @@ public void invalidate() { isValid = false; } - // No-op method needed to implement SessionTransaction interface. + /** No-op method needed to implement SessionTransaction interface. */ @Override - public void setSpan(Span span) {} + public void setSpan(ISpan span) {} private Duration tryUpdateTimeout(final Duration timeout, final Stopwatch stopwatch) { final Duration remainingTimeout = diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionClient.java index 474d99671e8..b294ef33395 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionClient.java @@ -25,8 +25,6 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; -import io.opencensus.common.Scope; -import io.opencensus.trace.Span; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -127,16 +125,17 @@ private BatchCreateSessionsRunnable( public void run() { List sessions; int remainingSessionsToCreate = sessionCount; - Span span = SpannerImpl.tracer.spanBuilder(SpannerImpl.BATCH_CREATE_SESSIONS).startSpan(); - try (Scope s = SpannerImpl.tracer.withSpan(span)) { - SpannerImpl.tracer + ISpan span = spanner.getTracer().spanBuilder(SpannerImpl.BATCH_CREATE_SESSIONS); + try (IScope s = spanner.getTracer().withSpan(span)) { + spanner + .getTracer() .getCurrentSpan() .addAnnotation(String.format("Creating %d sessions", sessionCount)); while (remainingSessionsToCreate > 0) { try { sessions = internalBatchCreateSessions(remainingSessionsToCreate, channelHint); } catch (Throwable t) { - TraceUtil.setWithFailure(SpannerImpl.tracer.getCurrentSpan(), t); + spanner.getTracer().getCurrentSpan().setStatus(t); consumer.onSessionCreateFailure(t, remainingSessionsToCreate); break; } @@ -146,7 +145,7 @@ public void run() { remainingSessionsToCreate -= sessions.size(); } } finally { - span.end(TraceUtil.END_SPAN_OPTIONS); + span.end(); } } } @@ -206,8 +205,8 @@ SessionImpl createSession() { synchronized (this) { options = optionMap(SessionOption.channelHint(sessionChannelCounter++)); } - Span span = SpannerImpl.tracer.spanBuilder(SpannerImpl.CREATE_SESSION).startSpan(); - try (Scope s = SpannerImpl.tracer.withSpan(span)) { + ISpan span = spanner.getTracer().spanBuilder(SpannerImpl.CREATE_SESSION); + try (IScope s = spanner.getTracer().withSpan(span)) { com.google.spanner.v1.Session session = spanner .getRpc() @@ -218,10 +217,10 @@ SessionImpl createSession() { options); return new SessionImpl(spanner, session.getName(), options); } catch (RuntimeException e) { - TraceUtil.setWithFailure(span, e); + span.setStatus(e); throw e; } finally { - span.end(TraceUtil.END_SPAN_OPTIONS); + span.end(); } } @@ -290,13 +289,13 @@ void asyncBatchCreateSessions( private List internalBatchCreateSessions( final int sessionCount, final long channelHint) throws SpannerException { final Map options = optionMap(SessionOption.channelHint(channelHint)); - Span parent = SpannerImpl.tracer.getCurrentSpan(); - Span span = - SpannerImpl.tracer - .spanBuilderWithExplicitParent(SpannerImpl.BATCH_CREATE_SESSIONS_REQUEST, parent) - .startSpan(); + ISpan parent = spanner.getTracer().getCurrentSpan(); + ISpan span = + spanner + .getTracer() + .spanBuilderWithExplicitParent(SpannerImpl.BATCH_CREATE_SESSIONS_REQUEST, parent); span.addAnnotation(String.format("Requesting %d sessions", sessionCount)); - try (Scope s = SpannerImpl.tracer.withSpan(span)) { + try (IScope s = spanner.getTracer().withSpan(span)) { List sessions = spanner .getRpc() @@ -309,14 +308,15 @@ private List internalBatchCreateSessions( span.addAnnotation( String.format( "Request for %d sessions returned %d sessions", sessionCount, sessions.size())); - span.end(TraceUtil.END_SPAN_OPTIONS); + span.end(); List res = new ArrayList<>(sessionCount); for (com.google.spanner.v1.Session session : sessions) { res.add(new SessionImpl(spanner, session.getName(), options)); } return res; } catch (RuntimeException e) { - TraceUtil.endSpanWithFailure(span, e); + span.setStatus(e); + span.end(); throw e; } } 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 53bf37feb05..29928f61cec 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 @@ -35,6 +35,7 @@ import com.google.common.collect.Lists; import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.ByteString; +import com.google.protobuf.Duration; import com.google.protobuf.Empty; import com.google.spanner.v1.BatchWriteRequest; import com.google.spanner.v1.BatchWriteResponse; @@ -43,10 +44,6 @@ import com.google.spanner.v1.RequestOptions; import com.google.spanner.v1.Transaction; import com.google.spanner.v1.TransactionOptions; -import io.opencensus.common.Scope; -import io.opencensus.trace.Span; -import io.opencensus.trace.Tracer; -import io.opencensus.trace.Tracing; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -60,7 +57,7 @@ * users need not be aware of the actual session management, pooling and handling. */ class SessionImpl implements Session { - private static final Tracer tracer = Tracing.getTracer(); + private final TraceWrapper tracer; /** Keep track of running transactions on this session per thread. */ static final ThreadLocal hasPendingTransaction = ThreadLocal.withInitial(() -> false); @@ -86,10 +83,12 @@ static TransactionOptions createReadWriteTransactionOptions(Options options) { * only have one such transaction active at a time. */ interface SessionTransaction { + /** Invalidates the transaction, generally because a new one has been started on the session. */ void invalidate(); + /** Registers the current span on the transaction. */ - void setSpan(Span span); + void setSpan(ISpan span); } private final SpannerImpl spanner; @@ -98,11 +97,12 @@ interface SessionTransaction { private SessionTransaction activeTransaction; ByteString readyTransactionId; private final Map options; - private Span currentSpan; private volatile Instant lastUseTime; + private ISpan currentSpan; SessionImpl(SpannerImpl spanner, String name, Map options) { this.spanner = spanner; + this.tracer = spanner.getTracer(); this.options = options; this.name = checkNotNull(name); this.databaseId = SessionId.of(name).getDatabaseId(); @@ -118,11 +118,11 @@ public String getName() { return options; } - void setCurrentSpan(Span span) { + void setCurrentSpan(ISpan span) { currentSpan = span; } - Span getCurrentSpan() { + ISpan getCurrentSpan() { return currentSpan; } @@ -176,29 +176,37 @@ public CommitResponse writeAtLeastOnceWithOptions( setActive(null); List mutationsProto = new ArrayList<>(); Mutation.toProto(mutations, mutationsProto); + Options options = Options.fromTransactionOptions(transactionOptions); final CommitRequest.Builder requestBuilder = CommitRequest.newBuilder() .setSession(name) - .setReturnCommitStats( - Options.fromTransactionOptions(transactionOptions).withCommitStats()) + .setReturnCommitStats(options.withCommitStats()) .addAllMutations(mutationsProto) .setSingleUseTransaction( TransactionOptions.newBuilder() .setReadWrite(TransactionOptions.ReadWrite.getDefaultInstance())); + if (options.hasMaxCommitDelay()) { + requestBuilder.setMaxCommitDelay( + Duration.newBuilder() + .setSeconds(options.maxCommitDelay().getSeconds()) + .setNanos(options.maxCommitDelay().getNano()) + .build()); + } RequestOptions commitRequestOptions = getRequestOptions(transactionOptions); + if (commitRequestOptions != null) { requestBuilder.setRequestOptions(commitRequestOptions); } CommitRequest request = requestBuilder.build(); - Span span = tracer.spanBuilder(SpannerImpl.COMMIT).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(SpannerImpl.COMMIT); + try (IScope s = tracer.withSpan(span)) { return SpannerRetryHelper.runTxWithRetriesOnAborted( () -> new CommitResponse(spanner.getRpc().commit(request, this.options))); } catch (RuntimeException e) { - TraceUtil.setWithFailure(span, e); + span.setStatus(e); throw e; } finally { - span.end(TraceUtil.END_SPAN_OPTIONS); + span.end(); } } @@ -230,14 +238,14 @@ public ServerStream batchWriteAtLeastOnce( if (batchWriteRequestOptions != null) { requestBuilder.setRequestOptions(batchWriteRequestOptions); } - Span span = tracer.spanBuilder(SpannerImpl.BATCH_WRITE).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(SpannerImpl.BATCH_WRITE); + try (IScope s = tracer.withSpan(span)) { return spanner.getRpc().batchWriteAtLeastOnce(requestBuilder.build(), this.options); } catch (Throwable e) { - TraceUtil.setWithFailure(span, e); + span.setStatus(e); throw SpannerExceptionFactory.newSpannerException(e); } finally { - span.end(TraceUtil.END_SPAN_OPTIONS); + span.end(); } } @@ -257,6 +265,7 @@ public ReadContext singleUse(TimestampBound bound) { .setDefaultPrefetchChunks(spanner.getDefaultPrefetchChunks()) .setDefaultDirectedReadOptions(spanner.getOptions().getDirectedReadOptions()) .setSpan(currentSpan) + .setTracer(tracer) .setExecutorProvider(spanner.getAsyncExecutorProvider()) .build()); } @@ -277,6 +286,7 @@ public ReadOnlyTransaction singleUseReadOnlyTransaction(TimestampBound bound) { .setDefaultPrefetchChunks(spanner.getDefaultPrefetchChunks()) .setDefaultDirectedReadOptions(spanner.getOptions().getDirectedReadOptions()) .setSpan(currentSpan) + .setTracer(tracer) .setExecutorProvider(spanner.getAsyncExecutorProvider()) .buildSingleUseReadOnlyTransaction()); } @@ -297,6 +307,7 @@ public ReadOnlyTransaction readOnlyTransaction(TimestampBound bound) { .setDefaultPrefetchChunks(spanner.getDefaultPrefetchChunks()) .setDefaultDirectedReadOptions(spanner.getOptions().getDirectedReadOptions()) .setSpan(currentSpan) + .setTracer(tracer) .setExecutorProvider(spanner.getAsyncExecutorProvider()) .build()); } @@ -313,7 +324,7 @@ public AsyncRunner runAsync(TransactionOption... options) { @Override public TransactionManager transactionManager(TransactionOption... options) { - return new TransactionManagerImpl(this, currentSpan, options); + return new TransactionManagerImpl(this, currentSpan, tracer, options); } @Override @@ -334,14 +345,14 @@ public ApiFuture asyncClose() { @Override public void close() { - Span span = tracer.spanBuilder(SpannerImpl.DELETE_SESSION).startSpan(); - try (Scope s = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(SpannerImpl.DELETE_SESSION); + try (IScope s = tracer.withSpan(span)) { spanner.getRpc().deleteSession(name, options); } catch (RuntimeException e) { - TraceUtil.setWithFailure(span, e); + span.setStatus(e); throw e; } finally { - span.end(TraceUtil.END_SPAN_OPTIONS); + span.end(); } } @@ -361,7 +372,7 @@ ApiFuture beginTransactionAsync(boolean routeToLeader) { ApiFuture beginTransactionAsync(Options transactionOptions, boolean routeToLeader) { final SettableApiFuture res = SettableApiFuture.create(); - final Span span = tracer.spanBuilder(SpannerImpl.BEGIN_TRANSACTION).startSpan(); + final ISpan span = tracer.spanBuilder(SpannerImpl.BEGIN_TRANSACTION); final BeginTransactionRequest request = BeginTransactionRequest.newBuilder() .setSession(name) @@ -370,30 +381,31 @@ ApiFuture beginTransactionAsync(Options transactionOptions, boolean final ApiFuture requestFuture = spanner.getRpc().beginTransactionAsync(request, options, routeToLeader); requestFuture.addListener( - tracer.withSpan( - span, - () -> { - try { - Transaction txn = requestFuture.get(); - if (txn.getId().isEmpty()) { - throw newSpannerException( - ErrorCode.INTERNAL, "Missing id in transaction\n" + getName()); - } - span.end(TraceUtil.END_SPAN_OPTIONS); - res.set(txn.getId()); - } catch (ExecutionException e) { - TraceUtil.endSpanWithFailure(span, e); - res.setException( - SpannerExceptionFactory.newSpannerException( - e.getCause() == null ? e : e.getCause())); - } catch (InterruptedException e) { - TraceUtil.endSpanWithFailure(span, e); - res.setException(SpannerExceptionFactory.propagateInterrupt(e)); - } catch (Exception e) { - TraceUtil.endSpanWithFailure(span, e); - res.setException(e); - } - }), + () -> { + try (IScope s = tracer.withSpan(span)) { + Transaction txn = requestFuture.get(); + if (txn.getId().isEmpty()) { + throw newSpannerException( + ErrorCode.INTERNAL, "Missing id in transaction\n" + getName()); + } + span.end(); + res.set(txn.getId()); + } catch (ExecutionException e) { + span.setStatus(e); + span.end(); + res.setException( + SpannerExceptionFactory.newSpannerException( + e.getCause() == null ? e : e.getCause())); + } catch (InterruptedException e) { + span.setStatus(e); + span.end(); + res.setException(SpannerExceptionFactory.propagateInterrupt(e)); + } catch (Exception e) { + span.setStatus(e); + span.end(); + res.setException(e); + } + }, MoreExecutors.directExecutor()); return res; } @@ -412,6 +424,7 @@ TransactionContextImpl newTransaction(Options options) { .setDefaultQueryOptions(spanner.getDefaultQueryOptions(databaseId)) .setDefaultPrefetchChunks(spanner.getDefaultPrefetchChunks()) .setSpan(currentSpan) + .setTracer(tracer) .setExecutorProvider(spanner.getAsyncExecutorProvider()) .setClock(poolMaintainerClock == null ? new Clock() : poolMaintainerClock) .build(); @@ -434,4 +447,8 @@ T setActive(@Nullable T ctx) { boolean hasReadyTransaction() { return readyTransactionId != null; } + + TraceWrapper getTracer() { + return tracer; + } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java index 54a0a292cd8..cc24dd2ba0f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java @@ -22,17 +22,21 @@ import static com.google.cloud.spanner.MetricRegistryConstants.MAX_ALLOWED_SESSIONS_DESCRIPTION; import static com.google.cloud.spanner.MetricRegistryConstants.MAX_IN_USE_SESSIONS; import static com.google.cloud.spanner.MetricRegistryConstants.MAX_IN_USE_SESSIONS_DESCRIPTION; +import static com.google.cloud.spanner.MetricRegistryConstants.METRIC_PREFIX; import static com.google.cloud.spanner.MetricRegistryConstants.NUM_ACQUIRED_SESSIONS; import static com.google.cloud.spanner.MetricRegistryConstants.NUM_ACQUIRED_SESSIONS_DESCRIPTION; import static com.google.cloud.spanner.MetricRegistryConstants.NUM_IN_USE_SESSIONS; import static com.google.cloud.spanner.MetricRegistryConstants.NUM_READ_SESSIONS; import static com.google.cloud.spanner.MetricRegistryConstants.NUM_RELEASED_SESSIONS; import static com.google.cloud.spanner.MetricRegistryConstants.NUM_RELEASED_SESSIONS_DESCRIPTION; +import static com.google.cloud.spanner.MetricRegistryConstants.NUM_SESSIONS_AVAILABLE; import static com.google.cloud.spanner.MetricRegistryConstants.NUM_SESSIONS_BEING_PREPARED; import static com.google.cloud.spanner.MetricRegistryConstants.NUM_SESSIONS_IN_POOL; import static com.google.cloud.spanner.MetricRegistryConstants.NUM_SESSIONS_IN_POOL_DESCRIPTION; +import static com.google.cloud.spanner.MetricRegistryConstants.NUM_SESSIONS_IN_USE; import static com.google.cloud.spanner.MetricRegistryConstants.NUM_WRITE_SESSIONS; import static com.google.cloud.spanner.MetricRegistryConstants.SESSIONS_TIMEOUTS_DESCRIPTION; +import static com.google.cloud.spanner.MetricRegistryConstants.SESSIONS_TYPE; import static com.google.cloud.spanner.MetricRegistryConstants.SPANNER_DEFAULT_LABEL_VALUES; import static com.google.cloud.spanner.MetricRegistryConstants.SPANNER_LABEL_KEYS; import static com.google.cloud.spanner.MetricRegistryConstants.SPANNER_LABEL_KEYS_WITH_TYPE; @@ -62,7 +66,6 @@ import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.ForwardingListenableFuture; import com.google.common.util.concurrent.ForwardingListenableFuture.SimpleForwardingListenableFuture; import com.google.common.util.concurrent.ListenableFuture; @@ -71,20 +74,16 @@ import com.google.protobuf.Empty; import com.google.spanner.v1.BatchWriteResponse; import com.google.spanner.v1.ResultSetStats; -import io.opencensus.common.Scope; import io.opencensus.metrics.DerivedLongCumulative; import io.opencensus.metrics.DerivedLongGauge; import io.opencensus.metrics.LabelValue; import io.opencensus.metrics.MetricOptions; import io.opencensus.metrics.MetricRegistry; import io.opencensus.metrics.Metrics; -import io.opencensus.trace.Annotation; -import io.opencensus.trace.AttributeValue; -import io.opencensus.trace.BlankSpan; -import io.opencensus.trace.Span; -import io.opencensus.trace.Status; -import io.opencensus.trace.Tracer; -import io.opencensus.trace.Tracing; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.metrics.Meter; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; @@ -117,7 +116,7 @@ class SessionPool { private static final Logger logger = Logger.getLogger(SessionPool.class.getName()); - private static final Tracer tracer = Tracing.getTracer(); + private final TraceWrapper tracer; static final String WAIT_FOR_SESSION = "SessionPool.WaitForSession"; /** @@ -1094,7 +1093,7 @@ private enum SessionState { } private PooledSessionFuture createPooledSessionFuture( - ListenableFuture future, Span span) { + ListenableFuture future, ISpan span) { return new PooledSessionFuture(future, span); } @@ -1103,10 +1102,10 @@ class PooledSessionFuture extends SimpleForwardingListenableFuture delegate, Span span) { + PooledSessionFuture(ListenableFuture delegate, ISpan span) { super(delegate); this.span = span; } @@ -1338,7 +1337,7 @@ PooledSession get(final boolean eligibleForLongRunning) { } if (res != null) { res.markBusy(span); - span.addAnnotation(sessionAnnotation(res)); + span.addAnnotation("Using Session", "sessionId", res.getName()); synchronized (lock) { incrementNumSessionsInUse(); checkedOutSessions.add(this); @@ -1572,8 +1571,8 @@ public void prepareReadWriteTransaction() { private void keepAlive() { markUsed(); - final Span previousSpan = delegate.getCurrentSpan(); - delegate.setCurrentSpan(BlankSpan.INSTANCE); + final ISpan previousSpan = delegate.getCurrentSpan(); + delegate.setCurrentSpan(tracer.getBlankSpan()); try (ResultSet resultSet = delegate .singleUse(TimestampBound.ofMaxStaleness(60, TimeUnit.SECONDS)) @@ -1612,7 +1611,7 @@ private Dialect determineDialect() { } } - private void markBusy(Span span) { + private void markBusy(ISpan span) { this.delegate.setCurrentSpan(span); this.state = SessionState.BUSY; } @@ -1652,14 +1651,14 @@ private void put(SpannerException e) { public PooledSession get() { long currentTimeout = options.getInitialWaitForSessionTimeoutMillis(); while (true) { - Span span = tracer.spanBuilder(WAIT_FOR_SESSION).startSpan(); - try (Scope waitScope = tracer.withSpan(span)) { + ISpan span = tracer.spanBuilder(WAIT_FOR_SESSION); + try (IScope waitScope = tracer.withSpan(span)) { PooledSession s = pollUninterruptiblyWithTimeout(currentTimeout, options.getAcquireSessionTimeout()); if (s == null) { // Set the status to DEADLINE_EXCEEDED and retry. numWaiterTimeouts.incrementAndGet(); - tracer.getCurrentSpan().setStatus(Status.DEADLINE_EXCEEDED); + tracer.getCurrentSpan().setStatus(ErrorCode.DEADLINE_EXCEEDED); currentTimeout = Math.min(currentTimeout * 2, MAX_SESSION_WAIT_TIMEOUT); } else { return s; @@ -1668,12 +1667,12 @@ public PooledSession get() { if (e instanceof SpannerException && ErrorCode.RESOURCE_EXHAUSTED.equals(((SpannerException) e).getErrorCode())) { numWaiterTimeouts.incrementAndGet(); - tracer.getCurrentSpan().setStatus(Status.RESOURCE_EXHAUSTED); + tracer.getCurrentSpan().setStatus(ErrorCode.RESOURCE_EXHAUSTED); } - TraceUtil.setWithFailure(span, e); + span.setStatus(e); throw e; } finally { - span.end(TraceUtil.END_SPAN_OPTIONS); + span.end(); } } } @@ -2081,7 +2080,11 @@ enum Position { * be created. */ static SessionPool createPool( - SpannerOptions spannerOptions, SessionClient sessionClient, List labelValues) { + SpannerOptions spannerOptions, + SessionClient sessionClient, + TraceWrapper tracer, + List labelValues, + Attributes attributes) { final SessionPoolOptions sessionPoolOptions = spannerOptions.getSessionPoolOptions(); // A clock instance is passed in {@code SessionPoolOptions} in order to allow mocking via tests. @@ -2094,14 +2097,26 @@ static SessionPool createPool( poolMaintainerClock == null ? new Clock() : poolMaintainerClock, Position.RANDOM, Metrics.getMetricRegistry(), - labelValues); + tracer, + labelValues, + spannerOptions.getOpenTelemetry(), + attributes); } static SessionPool createPool( SessionPoolOptions poolOptions, ExecutorFactory executorFactory, - SessionClient sessionClient) { - return createPool(poolOptions, executorFactory, sessionClient, new Clock(), Position.RANDOM); + SessionClient sessionClient, + TraceWrapper tracer, + OpenTelemetry openTelemetry) { + return createPool( + poolOptions, + executorFactory, + sessionClient, + new Clock(), + Position.RANDOM, + tracer, + openTelemetry); } static SessionPool createPool( @@ -2109,7 +2124,9 @@ static SessionPool createPool( ExecutorFactory executorFactory, SessionClient sessionClient, Clock clock, - Position initialReleasePosition) { + Position initialReleasePosition, + TraceWrapper tracer, + OpenTelemetry openTelemetry) { return createPool( poolOptions, null, @@ -2118,7 +2135,10 @@ static SessionPool createPool( clock, initialReleasePosition, Metrics.getMetricRegistry(), - SPANNER_DEFAULT_LABEL_VALUES); + tracer, + SPANNER_DEFAULT_LABEL_VALUES, + openTelemetry, + null); } static SessionPool createPool( @@ -2129,7 +2149,10 @@ static SessionPool createPool( Clock clock, Position initialReleasePosition, MetricRegistry metricRegistry, - List labelValues) { + TraceWrapper tracer, + List labelValues, + OpenTelemetry openTelemetry, + Attributes attributes) { SessionPool pool = new SessionPool( poolOptions, @@ -2140,7 +2163,10 @@ static SessionPool createPool( clock, initialReleasePosition, metricRegistry, - labelValues); + tracer, + labelValues, + openTelemetry, + attributes); pool.initPool(); return pool; } @@ -2154,7 +2180,10 @@ private SessionPool( Clock clock, Position initialReleasePosition, MetricRegistry metricRegistry, - List labelValues) { + TraceWrapper tracer, + List labelValues, + OpenTelemetry openTelemetry, + Attributes attributes) { this.options = options; this.databaseRole = databaseRole; this.executorFactory = executorFactory; @@ -2163,7 +2192,9 @@ private SessionPool( this.clock = clock; this.initialReleasePosition = initialReleasePosition; this.poolMaintainer = new PoolMaintainer(); - this.initMetricsCollection(metricRegistry, labelValues); + this.tracer = tracer; + this.initOpenCensusMetricsCollection(metricRegistry, labelValues); + this.initOpenTelemetryMetricsCollection(openTelemetry, attributes); this.waitOnMinSessionsLatch = options.getMinSessions() > 0 ? new CountDownLatch(1) : new CountDownLatch(0); } @@ -2351,7 +2382,7 @@ boolean isValid() { * */ PooledSessionFuture getSession() throws SpannerException { - Span span = Tracing.getTracer().getCurrentSpan(); + ISpan span = tracer.getCurrentSpan(); span.addAnnotation("Acquiring session"); WaiterFuture waiter = null; PooledSession sess = null; @@ -2383,7 +2414,7 @@ PooledSessionFuture getSession() throws SpannerException { } private PooledSessionFuture checkoutSession( - final Span span, final PooledSession readySession, WaiterFuture waiter) { + final ISpan span, final PooledSession readySession, WaiterFuture waiter) { ListenableFuture sessionFuture; if (waiter != null) { logger.log( @@ -2416,12 +2447,6 @@ PooledSessionFuture replaceSession(SessionNotFoundException e, PooledSessionFutu } } - private Annotation sessionAnnotation(Session session) { - AttributeValue sessionId = AttributeValue.stringAttributeValue(session.getName()); - return Annotation.fromDescriptionAndAttributes( - "Using Session", ImmutableMap.of("sessionId", sessionId)); - } - private void incrementNumSessionsInUse() { synchronized (lock) { if (maxSessionsInUse < ++numSessionsInUse) { @@ -2432,7 +2457,7 @@ private void incrementNumSessionsInUse() { } private void maybeCreateSession() { - Span span = Tracing.getTracer().getCurrentSpan(); + ISpan span = tracer.getCurrentSpan(); synchronized (lock) { if (numWaiters() >= numSessionsBeingCreated) { if (canCreateSession()) { @@ -2799,13 +2824,17 @@ public void onSessionCreateFailure(Throwable t, int createFailureForSessionCount } /** - * Initializes and creates Spanner session relevant metrics. When coupled with an exporter, it - * allows users to monitor client behavior. + * Initializes and creates Spanner session relevant metrics using OpenCensus. When coupled with an + * exporter, it allows users to monitor client behavior. */ - private void initMetricsCollection(MetricRegistry metricRegistry, List labelValues) { + private void initOpenCensusMetricsCollection( + MetricRegistry metricRegistry, List labelValues) { + if (!SpannerOptions.isEnabledOpenCensusMetrics()) { + return; + } DerivedLongGauge maxInUseSessionsMetric = metricRegistry.addDerivedLongGauge( - MAX_IN_USE_SESSIONS, + METRIC_PREFIX + MAX_IN_USE_SESSIONS, MetricOptions.builder() .setDescription(MAX_IN_USE_SESSIONS_DESCRIPTION) .setUnit(COUNT) @@ -2814,7 +2843,7 @@ private void initMetricsCollection(MetricRegistry metricRegistry, List 0L); } + + /** + * Initializes and creates Spanner session relevant metrics using OpenTelemetry. When coupled with + * an exporter, it allows users to monitor client behavior. + */ + private void initOpenTelemetryMetricsCollection( + OpenTelemetry openTelemetry, Attributes attributes) { + if (openTelemetry == null || !SpannerOptions.isEnabledOpenTelemetryMetrics()) { + return; + } + + Meter meter = openTelemetry.getMeter(MetricRegistryConstants.INSTRUMENTATION_SCOPE); + meter + .gaugeBuilder(MAX_ALLOWED_SESSIONS) + .setDescription(MAX_ALLOWED_SESSIONS_DESCRIPTION) + .setUnit(COUNT) + .buildWithCallback( + measurement -> { + // Although Max sessions is a constant value, OpenTelemetry requires to define this as + // a callback. + measurement.record(options.getMaxSessions(), attributes); + }); + + meter + .gaugeBuilder(MAX_IN_USE_SESSIONS) + .setDescription(MAX_IN_USE_SESSIONS_DESCRIPTION) + .setUnit(COUNT) + .buildWithCallback( + measurement -> { + measurement.record(this.maxSessionsInUse, attributes); + }); + + AttributesBuilder attributesBuilder; + if (attributes != null) { + attributesBuilder = attributes.toBuilder(); + } else { + attributesBuilder = Attributes.builder(); + } + Attributes attributesInUseSessions = + attributesBuilder.put(SESSIONS_TYPE, NUM_SESSIONS_IN_USE).build(); + Attributes attributesAvailableSessions = + attributesBuilder.put(SESSIONS_TYPE, NUM_SESSIONS_AVAILABLE).build(); + meter + .upDownCounterBuilder(NUM_SESSIONS_IN_POOL) + .setDescription(NUM_SESSIONS_IN_POOL_DESCRIPTION) + .setUnit(COUNT) + .buildWithCallback( + measurement -> { + measurement.record(this.numSessionsInUse, attributesInUseSessions); + measurement.record(this.sessions.size(), attributesAvailableSessions); + }); + + meter + .counterBuilder(GET_SESSION_TIMEOUTS) + .setDescription(SESSIONS_TIMEOUTS_DESCRIPTION) + .setUnit(COUNT) + .buildWithCallback( + measurement -> { + measurement.record(this.getNumWaiterTimeouts(), attributes); + }); + + meter + .counterBuilder(NUM_ACQUIRED_SESSIONS) + .setDescription(NUM_ACQUIRED_SESSIONS_DESCRIPTION) + .setUnit(COUNT) + .buildWithCallback( + measurement -> { + measurement.record(this.numSessionsAcquired, attributes); + }); + + meter + .counterBuilder(NUM_RELEASED_SESSIONS) + .setDescription(NUM_RELEASED_SESSIONS_DESCRIPTION) + .setUnit(COUNT) + .buildWithCallback( + measurement -> { + measurement.record(this.numSessionsReleased, attributes); + }); + } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java index f1a7888c62c..326a51d803e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java @@ -37,8 +37,9 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions; import io.opencensus.metrics.LabelValue; -import io.opencensus.trace.Tracer; import io.opencensus.trace.Tracing; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -55,7 +56,14 @@ /** Default implementation of the Cloud Spanner interface. */ class SpannerImpl extends BaseService implements Spanner { private static final Logger logger = Logger.getLogger(SpannerImpl.class.getName()); - static final Tracer tracer = Tracing.getTracer(); + final TraceWrapper tracer = + new TraceWrapper( + Tracing.getTracer(), + this.getOptions() + .getOpenTelemetry() + .getTracer( + MetricRegistryConstants.INSTRUMENTATION_SCOPE, + GaxProperties.getLibraryVersion(this.getOptions().getClass()))); static final String CREATE_SESSION = "CloudSpannerOperation.CreateSession"; static final String BATCH_CREATE_SESSIONS = "CloudSpannerOperation.BatchCreateSessions"; @@ -148,6 +156,10 @@ QueryOptions getDefaultQueryOptions(DatabaseId databaseId) { return getOptions().getDefaultQueryOptions(databaseId); } + TraceWrapper getTracer() { + return this.tracer; + } + /** * Returns the {@link ExecutorProvider} to use for async methods that need a background executor. */ @@ -219,9 +231,19 @@ public DatabaseClient getDatabaseClient(DatabaseId db) { LabelValue.create(db.getDatabase()), LabelValue.create(db.getInstanceId().getName()), LabelValue.create(GaxProperties.getLibraryVersion(getOptions().getClass()))); + + AttributesBuilder attributesBuilder = Attributes.builder(); + attributesBuilder.put("client_id", clientId); + attributesBuilder.put("database", db.getDatabase()); + attributesBuilder.put("instance_id", db.getInstanceId().getName()); + SessionPool pool = SessionPool.createPool( - getOptions(), SpannerImpl.this.getSessionClient(db), labelValues); + getOptions(), + SpannerImpl.this.getSessionClient(db), + this.tracer, + labelValues, + attributesBuilder.build()); pool.maybeWaitOnMinSessions(); DatabaseClientImpl dbClient = createDatabaseClient(clientId, pool); dbClients.put(db, dbClient); @@ -232,7 +254,7 @@ public DatabaseClient getDatabaseClient(DatabaseId db) { @VisibleForTesting DatabaseClientImpl createDatabaseClient(String clientId, SessionPool pool) { - return new DatabaseClientImpl(clientId, pool); + return new DatabaseClientImpl(clientId, pool, tracer); } @Override 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 0d9f3b85c19..9c6044aa938 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,6 +19,7 @@ import com.google.api.core.ApiFunction; import com.google.api.core.BetaApi; import com.google.api.core.InternalApi; +import com.google.api.core.ObsoleteApi; import com.google.api.gax.core.ExecutorProvider; import com.google.api.gax.grpc.GrpcCallContext; import com.google.api.gax.grpc.GrpcInterceptorProvider; @@ -61,6 +62,8 @@ import io.grpc.ExperimentalApi; import io.grpc.ManagedChannelBuilder; import io.grpc.MethodDescriptor; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; @@ -75,12 +78,15 @@ import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; import org.threeten.bp.Duration; /** Options for the Cloud Spanner service. */ public class SpannerOptions extends ServiceOptions { private static final long serialVersionUID = 2789571558532701170L; private static SpannerEnvironment environment = SpannerEnvironmentImpl.INSTANCE; + private static boolean enableOpenCensusMetrics = true; + private static boolean enableOpenTelemetryMetrics = false; private static final String JDBC_API_CLIENT_LIB_TOKEN = "sp-jdbc"; private static final String HIBERNATE_API_CLIENT_LIB_TOKEN = "sp-hib"; @@ -141,6 +147,17 @@ public class SpannerOptions extends ServiceOptions { private final boolean attemptDirectPath; private final DirectedReadOptions directedReadOptions; private final boolean useVirtualThreads; + private final OpenTelemetry openTelemetry; + + enum TracingFramework { + OPEN_CENSUS, + OPEN_TELEMETRY + } + + private static final Object lock = new Object(); + + @GuardedBy("lock") + private static TracingFramework activeTracingFramework; /** Interface that can be used to provide {@link CallCredentials} to {@link SpannerOptions}. */ public interface CallCredentialsProvider { @@ -633,6 +650,7 @@ protected SpannerOptions(Builder builder) { attemptDirectPath = builder.attemptDirectPath; directedReadOptions = builder.directedReadOptions; useVirtualThreads = builder.useVirtualThreads; + openTelemetry = builder.openTelemetry; } /** @@ -737,6 +755,7 @@ public static class Builder private boolean attemptDirectPath = true; private DirectedReadOptions directedReadOptions; private boolean useVirtualThreads = false; + private OpenTelemetry openTelemetry; private static String createCustomClientLibToken(String token) { return token + " " + ServiceOptions.getGoogApiClientLibName(); @@ -1243,6 +1262,15 @@ public Builder setEmulatorHost(String emulatorHost) { return this; } + /** + * Sets OpenTelemetry object to be used for Spanner Metrics and Traces. GlobalOpenTelemetry will + * be used as fallback if this options is not set. + */ + public Builder setOpenTelemetry(OpenTelemetry openTelemetry) { + this.openTelemetry = openTelemetry; + return this; + } + /** * Enable leader aware routing. Leader aware routing would route all requests in RW/PDML * transactions to the leader region. @@ -1297,6 +1325,11 @@ public SpannerOptions build() { this.grpcGcpExtensionEnabled ? GRPC_GCP_ENABLED_DEFAULT_CHANNELS : DEFAULT_CHANNELS; } + synchronized (lock) { + if (activeTracingFramework == null) { + activeTracingFramework = TracingFramework.OPEN_CENSUS; + } + } return new SpannerOptions(this); } } @@ -1326,6 +1359,77 @@ public static void useDefaultEnvironment() { SpannerOptions.environment = SpannerEnvironmentImpl.INSTANCE; } + /** + * Enables OpenTelemetry traces. Enabling OpenTelemetry traces will disable OpenCensus traces. By + * default, OpenCensus traces are enabled. + */ + public static void enableOpenTelemetryTraces() { + synchronized (lock) { + if (activeTracingFramework != null + && activeTracingFramework != TracingFramework.OPEN_TELEMETRY) { + throw new IllegalStateException( + "ActiveTracingFramework is set to OpenCensus and cannot be reset after SpannerOptions object is created."); + } + activeTracingFramework = TracingFramework.OPEN_TELEMETRY; + } + } + + /** Enables OpenCensus traces. Enabling OpenCensus traces will disable OpenTelemetry traces. */ + @ObsoleteApi( + "The OpenCensus project is deprecated. Use enableOpenTelemetryTraces to switch to OpenTelemetry traces") + public static void enableOpenCensusTraces() { + synchronized (lock) { + if (activeTracingFramework != null + && activeTracingFramework != TracingFramework.OPEN_CENSUS) { + throw new IllegalStateException( + "ActiveTracingFramework is set to OpenTelemetry and cannot be reset after SpannerOptions object is created."); + } + activeTracingFramework = TracingFramework.OPEN_CENSUS; + } + } + + /** + * Always resets the activeTracingFramework. This variable is used for internal testing, and is + * not a valid production scenario + */ + @ObsoleteApi( + "The OpenCensus project is deprecated. Use enableOpenTelemetryTraces to switch to OpenTelemetry traces") + static void resetActiveTracingFramework() { + activeTracingFramework = null; + } + + public static TracingFramework getActiveTracingFramework() { + synchronized (lock) { + if (activeTracingFramework == null) { + return TracingFramework.OPEN_CENSUS; + } + return activeTracingFramework; + } + } + + /** Disables OpenCensus metrics. Disable OpenCensus metrics before creating Spanner client. */ + public static void disableOpenCensusMetrics() { + SpannerOptions.enableOpenCensusMetrics = false; + } + + @VisibleForTesting + static void enableOpenCensusMetrics() { + SpannerOptions.enableOpenCensusMetrics = true; + } + + public static boolean isEnabledOpenCensusMetrics() { + return SpannerOptions.enableOpenCensusMetrics; + } + + /** Enables OpenTelemetry metrics. Enable OpenTelemetry metrics before creating Spanner client. */ + public static void enableOpenTelemetryMetrics() { + SpannerOptions.enableOpenTelemetryMetrics = true; + } + + public static boolean isEnabledOpenTelemetryMetrics() { + return SpannerOptions.enableOpenTelemetryMetrics; + } + @Override protected String getDefaultProject() { String projectId = getDefaultProjectId(); @@ -1426,6 +1530,18 @@ public boolean isAttemptDirectPath() { return attemptDirectPath; } + /** + * Returns an instance of OpenTelemetry. If OpenTelemetry object is not set via SpannerOptions + * then GlobalOpenTelemetry will be used as fallback. + */ + public OpenTelemetry getOpenTelemetry() { + if (this.openTelemetry != null) { + return this.openTelemetry; + } else { + return GlobalOpenTelemetry.get(); + } + } + @BetaApi public boolean isUseVirtualThreads() { return useVirtualThreads; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerRpcMetrics.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerRpcMetrics.java new file mode 100644 index 00000000000..794c211971d --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerRpcMetrics.java @@ -0,0 +1,74 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.spanner; + +import com.google.api.core.InternalApi; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.Meter; +import java.util.Arrays; +import java.util.List; + +@InternalApi +public class SpannerRpcMetrics { + private final LongHistogram gfeLatencies; + private final LongCounter gfeHeaderMissingCount; + + public SpannerRpcMetrics(OpenTelemetry openTelemetry) { + if (!SpannerOptions.isEnabledOpenTelemetryMetrics()) { + gfeLatencies = null; + gfeHeaderMissingCount = null; + return; + } + + Meter meter = openTelemetry.getMeter(MetricRegistryConstants.INSTRUMENTATION_SCOPE); + List RPC_MILLIS_BUCKET_BOUNDARIES = + Arrays.asList( + 1L, 2L, 3L, 4L, 5L, 6L, 8L, 10L, 13L, 16L, 20L, 25L, 30L, 40L, 50L, 65L, 80L, 100L, + 130L, 160L, 200L, 250L, 300L, 400L, 500L, 650L, 800L, 1000L, 2000L, 5000L, 10000L, + 20000L, 50000L, 100000L); + gfeLatencies = + meter + .histogramBuilder(MetricRegistryConstants.SPANNER_GFE_LATENCY) + .ofLongs() + .setDescription(MetricRegistryConstants.SPANNER_GFE_LATENCY_DESCRIPTION) + .setUnit("ms") + .setExplicitBucketBoundariesAdvice(RPC_MILLIS_BUCKET_BOUNDARIES) + .build(); + gfeHeaderMissingCount = + meter + .counterBuilder(MetricRegistryConstants.SPANNER_GFE_HEADER_MISSING_COUNT) + .setDescription(MetricRegistryConstants.SPANNER_GFE_HEADER_MISSING_COUNT_DESCRIPTION) + .setUnit(MetricRegistryConstants.COUNT) + .build(); + } + + @InternalApi + public void recordGfeLatency(long value, Attributes attributes) { + if (gfeLatencies != null) { + gfeLatencies.record(value, attributes); + } + } + + @InternalApi + public void recordGfeHeaderMissingCount(long value, Attributes attributes) { + if (gfeHeaderMissingCount != null) { + gfeHeaderMissingCount.add(value, attributes); + } + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ThreadFactoryUtil.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ThreadFactoryUtil.java index 67f4d3230d4..72d58e85be3 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ThreadFactoryUtil.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ThreadFactoryUtil.java @@ -69,6 +69,11 @@ public static ThreadFactory tryCreateVirtualThreadFactory(String baseNameFormat) } catch (ClassNotFoundException | NoSuchMethodException ignore) { return null; } catch (InvocationTargetException | IllegalAccessException e) { + // Java 20 supports virtual threads as an experimental feature. It will throw an + // UnsupportedOperationException if experimental features have not been enabled. + if (e.getCause() instanceof UnsupportedOperationException) { + return null; + } throw new RuntimeException(e); } } @@ -91,6 +96,11 @@ public static ExecutorService tryCreateVirtualThreadPerTaskExecutor(String baseN } catch (NoSuchMethodException ignore) { return null; } catch (InvocationTargetException | IllegalAccessException e) { + // Java 20 supports virtual threads as an experimental feature. It will throw an + // UnsupportedOperationException if experimental features have not been enabled. + if (e.getCause() instanceof UnsupportedOperationException) { + return null; + } throw new RuntimeException(e); } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TraceUtil.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TraceUtil.java deleted file mode 100644 index 0d429661ad2..00000000000 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TraceUtil.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2017 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.cloud.Timestamp; -import com.google.common.collect.ImmutableMap; -import com.google.spanner.v1.Transaction; -import io.opencensus.contrib.grpc.util.StatusConverter; -import io.opencensus.trace.AttributeValue; -import io.opencensus.trace.EndSpanOptions; -import io.opencensus.trace.Span; -import io.opencensus.trace.Status; -import java.util.Map; - -/** Utility methods for tracing. */ -class TraceUtil { - - static final EndSpanOptions END_SPAN_OPTIONS = - EndSpanOptions.builder().setSampleToLocalSpanStore(true).build(); - - static Map getTransactionAnnotations(Transaction t) { - return ImmutableMap.of( - "Id", - AttributeValue.stringAttributeValue(t.getId().toStringUtf8()), - "Timestamp", - AttributeValue.stringAttributeValue(Timestamp.fromProto(t.getReadTimestamp()).toString())); - } - - static ImmutableMap getExceptionAnnotations(Throwable e) { - if (e instanceof SpannerException) { - return ImmutableMap.of( - "Status", - AttributeValue.stringAttributeValue(((SpannerException) e).getErrorCode().toString())); - } - return ImmutableMap.of(); - } - - static ImmutableMap getExceptionAnnotations(SpannerException e) { - return ImmutableMap.of( - "Status", AttributeValue.stringAttributeValue(e.getErrorCode().toString())); - } - - static void setWithFailure(Span span, Throwable e) { - if (e instanceof SpannerException) { - span.setStatus( - StatusConverter.fromGrpcStatus(((SpannerException) e).getErrorCode().getGrpcStatus()) - .withDescription(e.getMessage())); - } else { - span.setStatus(Status.INTERNAL.withDescription(e.getMessage())); - } - } - - static void endSpanWithFailure(Span span, Throwable e) { - if (e instanceof SpannerException) { - endSpanWithFailure(span, (SpannerException) e); - } else { - span.setStatus(Status.INTERNAL.withDescription(e.getMessage())); - span.end(END_SPAN_OPTIONS); - } - } - - static void endSpanWithFailure(Span span, SpannerException e) { - span.setStatus( - StatusConverter.fromGrpcStatus(e.getErrorCode().getGrpcStatus()) - .withDescription(e.getMessage())); - span.end(END_SPAN_OPTIONS); - } -} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TraceWrapper.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TraceWrapper.java new file mode 100644 index 00000000000..25796968e9e --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TraceWrapper.java @@ -0,0 +1,109 @@ +/* + * Copyright 2023 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.cloud.spanner.SpannerOptions.TracingFramework; +import io.opencensus.trace.BlankSpan; +import io.opencensus.trace.Span; +import io.opencensus.trace.Tracer; +import io.opentelemetry.context.Context; + +class TraceWrapper { + + private final Tracer openCensusTracer; + private final io.opentelemetry.api.trace.Tracer openTelemetryTracer; + + TraceWrapper(Tracer openCensusTracer, io.opentelemetry.api.trace.Tracer openTelemetryTracer) { + this.openTelemetryTracer = openTelemetryTracer; + this.openCensusTracer = openCensusTracer; + } + + ISpan spanBuilder(String spanName) { + if (SpannerOptions.getActiveTracingFramework().equals(TracingFramework.OPEN_TELEMETRY)) { + return new OpenTelemetrySpan(openTelemetryTracer.spanBuilder(spanName).startSpan()); + } else { + return new OpenCensusSpan(openCensusTracer.spanBuilder(spanName).startSpan()); + } + } + + ISpan spanBuilderWithExplicitParent(String spanName, ISpan parentSpan) { + if (SpannerOptions.getActiveTracingFramework().equals(TracingFramework.OPEN_TELEMETRY)) { + OpenTelemetrySpan otParentSpan = (OpenTelemetrySpan) parentSpan; + + io.opentelemetry.api.trace.Span otSpan; + + if (otParentSpan != null && otParentSpan.getOpenTelemetrySpan() != null) { + otSpan = + openTelemetryTracer + .spanBuilder(spanName) + .setParent(Context.current().with(otParentSpan.getOpenTelemetrySpan())) + .startSpan(); + } else { + otSpan = openTelemetryTracer.spanBuilder(spanName).startSpan(); + } + + return new OpenTelemetrySpan(otSpan); + + } else { + OpenCensusSpan parentOcSpan = (OpenCensusSpan) parentSpan; + Span ocSpan = + openCensusTracer + .spanBuilderWithExplicitParent( + spanName, parentOcSpan != null ? parentOcSpan.getOpenCensusSpan() : null) + .startSpan(); + + return new OpenCensusSpan(ocSpan); + } + } + + ISpan getCurrentSpan() { + if (SpannerOptions.getActiveTracingFramework().equals(TracingFramework.OPEN_TELEMETRY)) { + return new OpenTelemetrySpan( + io.opentelemetry.api.trace.Span.fromContext(io.opentelemetry.context.Context.current())); + } else { + return new OpenCensusSpan(openCensusTracer.getCurrentSpan()); + } + } + + ISpan getBlankSpan() { + if (SpannerOptions.getActiveTracingFramework().equals(TracingFramework.OPEN_TELEMETRY)) { + return new OpenTelemetrySpan(io.opentelemetry.api.trace.Span.getInvalid()); + } else { + return new OpenCensusSpan(BlankSpan.INSTANCE); + } + } + + IScope withSpan(ISpan span) { + if (SpannerOptions.getActiveTracingFramework().equals(TracingFramework.OPEN_TELEMETRY)) { + OpenTelemetrySpan openTelemetrySpan; + if (!(span instanceof OpenTelemetrySpan)) { + openTelemetrySpan = new OpenTelemetrySpan(null); + } else { + openTelemetrySpan = (OpenTelemetrySpan) span; + } + return new OpenTelemetryScope(openTelemetrySpan.getOpenTelemetrySpan().makeCurrent()); + } else { + OpenCensusSpan openCensusSpan; + if (!(span instanceof OpenCensusSpan)) { + openCensusSpan = new OpenCensusSpan(null); + } else { + openCensusSpan = (OpenCensusSpan) span; + } + return new OpenCensusScope(openCensusTracer.withSpan(openCensusSpan.getOpenCensusSpan())); + } + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java index 2d47fcd5c78..95ffd1168b2 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java @@ -20,41 +20,39 @@ import com.google.cloud.spanner.Options.TransactionOption; import com.google.cloud.spanner.SessionImpl.SessionTransaction; import com.google.common.base.Preconditions; -import io.opencensus.common.Scope; -import io.opencensus.trace.Span; -import io.opencensus.trace.Tracer; -import io.opencensus.trace.Tracing; /** Implementation of {@link TransactionManager}. */ final class TransactionManagerImpl implements TransactionManager, SessionTransaction { - private static final Tracer tracer = Tracing.getTracer(); + private final TraceWrapper tracer; private final SessionImpl session; - private Span span; + private ISpan span; private final Options options; private TransactionRunnerImpl.TransactionContextImpl txn; private TransactionState txnState; - TransactionManagerImpl(SessionImpl session, Span span, TransactionOption... options) { + TransactionManagerImpl( + SessionImpl session, ISpan span, TraceWrapper tracer, TransactionOption... options) { this.session = session; this.span = span; + this.tracer = tracer; this.options = Options.fromTransactionOptions(options); } - Span getSpan() { + ISpan getSpan() { return span; } @Override - public void setSpan(Span span) { + public void setSpan(ISpan span) { this.span = span; } @Override public TransactionContext begin() { Preconditions.checkState(txn == null, "begin can only be called once"); - try (Scope s = tracer.withSpan(span)) { + try (IScope s = tracer.withSpan(span)) { txn = session.newTransaction(options); session.setActive(this); txnState = TransactionState.STARTED; @@ -102,7 +100,7 @@ public TransactionContext resetForRetry() { throw new IllegalStateException( "resetForRetry can only be called if the previous attempt" + " aborted"); } - try (Scope s = tracer.withSpan(span)) { + try (IScope s = tracer.withSpan(span)) { boolean useInlinedBegin = txn.transactionId != null; txn = session.newTransaction(options); if (!useInlinedBegin) { @@ -137,7 +135,7 @@ public void close() { txnState = TransactionState.ROLLED_BACK; } } finally { - span.end(TraceUtil.END_SPAN_OPTIONS); + span.end(); } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java index 21c74a400f0..3249be1bdb3 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java @@ -49,11 +49,6 @@ import com.google.spanner.v1.Transaction; import com.google.spanner.v1.TransactionOptions; import com.google.spanner.v1.TransactionSelector; -import io.opencensus.common.Scope; -import io.opencensus.trace.AttributeValue; -import io.opencensus.trace.Span; -import io.opencensus.trace.Tracer; -import io.opencensus.trace.Tracing; import java.util.ArrayList; import java.util.List; import java.util.Queue; @@ -71,7 +66,6 @@ /** Default implementation of {@link TransactionRunner}. */ class TransactionRunnerImpl implements SessionTransaction, TransactionRunner { - private static final Tracer tracer = Tracing.getTracer(); private static final Logger txnLogger = Logger.getLogger(TransactionRunner.class.getName()); /** * (Part of) the error message that is returned by Cloud Spanner if a transaction is cancelled @@ -84,6 +78,7 @@ class TransactionRunnerImpl implements SessionTransaction, TransactionRunner { @VisibleForTesting static class TransactionContextImpl extends AbstractReadContext implements TransactionContext { + static class Builder extends AbstractReadContext.Builder { private Clock clock = new Clock(); @@ -131,6 +126,7 @@ static Builder newBuilder() { */ private class TransactionContextAsyncResultSetImpl extends ForwardingAsyncResultSet implements ListenableAsyncResultSet { + private TransactionContextAsyncResultSetImpl(ListenableAsyncResultSet delegate) { super(delegate); } @@ -254,10 +250,7 @@ ApiFuture ensureTxnAsync() { if (transactionId == null || isAborted()) { createTxnAsync(res); } else { - span.addAnnotation( - "Transaction Initialized", - ImmutableMap.of( - "Id", AttributeValue.stringAttributeValue(transactionId.toStringUtf8()))); + span.addAnnotation("Transaction Initialized", "Id", transactionId.toStringUtf8()); txnLogger.log( Level.FINER, "Using prepared transaction {0}", @@ -274,10 +267,7 @@ private void createTxnAsync(final SettableApiFuture res) { () -> { try { transactionId = fut.get(); - span.addAnnotation( - "Transaction Creation Done", - ImmutableMap.of( - "Id", AttributeValue.stringAttributeValue(transactionId.toStringUtf8()))); + span.addAnnotation("Transaction Creation Done", "Id", transactionId.toStringUtf8()); txnLogger.log( Level.FINER, "Started transaction {0}", @@ -285,8 +275,7 @@ private void createTxnAsync(final SettableApiFuture res) { res.set(null); } catch (ExecutionException e) { span.addAnnotation( - "Transaction Creation Failed", - TraceUtil.getExceptionAnnotations(e.getCause() == null ? e : e.getCause())); + "Transaction Creation Failed", e.getCause() == null ? e : e.getCause()); res.setException(e.getCause() == null ? e : e.getCause()); } catch (InterruptedException e) { res.setException(SpannerExceptionFactory.propagateInterrupt(e)); @@ -339,6 +328,13 @@ ApiFuture commitAsync() { } builder.setRequestOptions(requestOptionsBuilder.build()); } + if (options.hasMaxCommitDelay()) { + builder.setMaxCommitDelay( + com.google.protobuf.Duration.newBuilder() + .setSeconds(options.maxCommitDelay().getSeconds()) + .setNanos(options.maxCommitDelay().getNano()) + .build()); + } synchronized (lock) { if (transactionIdFuture == null && transactionId == null && runningAsyncOperations == 0) { finishOps = SettableApiFuture.create(); @@ -354,6 +350,7 @@ ApiFuture commitAsync() { } private final class CommitRunnable implements Runnable { + private final SettableApiFuture res; private final ApiFuture prev; private final CommitRequest.Builder requestBuilder; @@ -394,39 +391,37 @@ public void run() { } final CommitRequest commitRequest = requestBuilder.build(); span.addAnnotation("Starting Commit"); - final Span opSpan = - tracer.spanBuilderWithExplicitParent(SpannerImpl.COMMIT, span).startSpan(); + final ISpan opSpan = tracer.spanBuilderWithExplicitParent(SpannerImpl.COMMIT, span); final ApiFuture commitFuture = rpc.commitAsync(commitRequest, session.getOptions()); session.markUsed(clock.instant()); commitFuture.addListener( - tracer.withSpan( - opSpan, - () -> { - try { - com.google.spanner.v1.CommitResponse proto = commitFuture.get(); - if (!proto.hasCommitTimestamp()) { - throw newSpannerException( - ErrorCode.INTERNAL, "Missing commitTimestamp:\n" + session.getName()); - } - span.addAnnotation("Commit Done"); - opSpan.end(TraceUtil.END_SPAN_OPTIONS); - res.set(new CommitResponse(proto)); - } catch (Throwable e) { - if (e instanceof ExecutionException) { - e = - SpannerExceptionFactory.newSpannerException( - e.getCause() == null ? e : e.getCause()); - } else if (e instanceof InterruptedException) { - e = SpannerExceptionFactory.propagateInterrupt((InterruptedException) e); - } else { - e = SpannerExceptionFactory.newSpannerException(e); - } - span.addAnnotation("Commit Failed", TraceUtil.getExceptionAnnotations(e)); - TraceUtil.endSpanWithFailure(opSpan, e); - res.setException(onError((SpannerException) e, false)); - } - }), + () -> { + try (IScope s = tracer.withSpan(opSpan)) { + com.google.spanner.v1.CommitResponse proto = commitFuture.get(); + if (!proto.hasCommitTimestamp()) { + throw newSpannerException( + ErrorCode.INTERNAL, "Missing commitTimestamp:\n" + session.getName()); + } + span.addAnnotation("Commit Done"); + opSpan.end(); + res.set(new CommitResponse(proto)); + } catch (Throwable e) { + if (e instanceof ExecutionException) { + e = + SpannerExceptionFactory.newSpannerException( + e.getCause() == null ? e : e.getCause()); + } else if (e instanceof InterruptedException) { + e = SpannerExceptionFactory.propagateInterrupt((InterruptedException) e); + } else { + e = SpannerExceptionFactory.newSpannerException(e); + } + span.addAnnotation("Commit Failed", e); + opSpan.setStatus(e); + opSpan.end(); + res.setException(onError((SpannerException) e, false)); + } + }, MoreExecutors.directExecutor()); } catch (InterruptedException e) { res.setException(SpannerExceptionFactory.propagateInterrupt(e)); @@ -435,6 +430,9 @@ public void run() { } catch (ExecutionException e) { res.setException( SpannerExceptionFactory.newSpannerException(e.getCause() == null ? e : e.getCause())); + } catch (Throwable e) { + res.setException( + SpannerExceptionFactory.newSpannerException(e.getCause() == null ? e : e.getCause())); } } } @@ -455,7 +453,7 @@ void rollback() { rollbackAsync().get(); } catch (ExecutionException e) { txnLogger.log(Level.FINE, "Exception during rollback", e); - span.addAnnotation("Rollback Failed", TraceUtil.getExceptionAnnotations(e)); + span.addAnnotation("Rollback Failed", e); } catch (InterruptedException e) { throw SpannerExceptionFactory.propagateInterrupt(e); } @@ -575,7 +573,9 @@ public void onTransactionMetadata(Transaction transaction, boolean shouldInclude @Nullable String getTransactionTag() { - if (this.options.hasTag()) return this.options.tag(); + if (this.options.hasTag()) { + return this.options.tag(); + } return null; } @@ -949,7 +949,8 @@ public ListenableAsyncResultSet executeQueryAsync( private boolean blockNestedTxn = true; private final SessionImpl session; private final Options options; - private Span span; + private ISpan span; + private TraceWrapper tracer; private TransactionContextImpl txn; private volatile boolean isValid = true; @@ -963,29 +964,31 @@ public TransactionRunner allowNestedTransaction() { this.session = session; this.options = Options.fromTransactionOptions(options); this.txn = session.newTransaction(this.options); + this.tracer = session.getTracer(); } @Override - public void setSpan(Span span) { + public void setSpan(ISpan span) { this.span = span; } @Nullable @Override public T run(TransactionCallable callable) { - try (Scope s = tracer.withSpan(span)) { + try (IScope s = tracer.withSpan(span)) { if (blockNestedTxn) { SessionImpl.hasPendingTransaction.set(Boolean.TRUE); } return runInternal(callable); } catch (RuntimeException e) { - TraceUtil.setWithFailure(span, e); + span.setStatus(e); throw e; } finally { // Remove threadLocal rather than set to FALSE to avoid a possible memory leak. // We also do this unconditionally in case a user has modified the flag when the transaction // was running. SessionImpl.hasPendingTransaction.remove(); + span.end(); } } @@ -1003,9 +1006,7 @@ private T runInternal(final TransactionCallable txCallable) { checkState( isValid, "TransactionRunner has been invalidated by a new operation on the session"); attempt.incrementAndGet(); - span.addAnnotation( - "Starting Transaction Attempt", - ImmutableMap.of("Attempt", AttributeValue.longAttributeValue(attempt.longValue()))); + span.addAnnotation("Starting Transaction Attempt", "Attempt", attempt.longValue()); // Only ensure that there is a transaction if we should not inline the beginTransaction // with the first statement. if (!useInlinedBegin) { @@ -1022,8 +1023,8 @@ private T runInternal(final TransactionCallable txCallable) { if (txn.isAborted() || (e instanceof AbortedException)) { span.addAnnotation( "Transaction Attempt Aborted in user operation. Retrying", - ImmutableMap.of( - "Attempt", AttributeValue.longAttributeValue(attempt.longValue()))); + "Attempt", + attempt.longValue()); shouldRollback = false; if (e instanceof AbortedException) { throw e; @@ -1039,10 +1040,8 @@ private T runInternal(final TransactionCallable txCallable) { } span.addAnnotation( "Transaction Attempt Failed in user operation", - ImmutableMap.builder() - .putAll(TraceUtil.getExceptionAnnotations(toThrow)) - .put("Attempt", AttributeValue.longAttributeValue(attempt.longValue())) - .build()); + ImmutableMap.of( + "Attempt", attempt.longValue(), "Status", toThrow.getErrorCode().toString())); throw toThrow; } finally { if (shouldRollback) { @@ -1052,23 +1051,18 @@ private T runInternal(final TransactionCallable txCallable) { try { txn.commit(); - span.addAnnotation( - "Transaction Attempt Succeeded", - ImmutableMap.of("Attempt", AttributeValue.longAttributeValue(attempt.longValue()))); + span.addAnnotation("Transaction Attempt Succeeded", "Attempt", attempt.longValue()); return result; } catch (AbortedException e) { txnLogger.log(Level.FINE, "Commit aborted", e); span.addAnnotation( - "Transaction Attempt Aborted in Commit. Retrying", - ImmutableMap.of("Attempt", AttributeValue.longAttributeValue(attempt.longValue()))); + "Transaction Attempt Aborted in Commit. Retrying", "Attempt", attempt.longValue()); throw e; } catch (SpannerException e) { span.addAnnotation( "Transaction Attempt Failed in Commit", - ImmutableMap.builder() - .putAll(TraceUtil.getExceptionAnnotations(e)) - .put("Attempt", AttributeValue.longAttributeValue(attempt.longValue())) - .build()); + ImmutableMap.of( + "Attempt", attempt.longValue(), "Status", e.getErrorCode().toString())); throw e; } }; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java index 629ab9cca6a..8fc3043791e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java @@ -470,7 +470,7 @@ ParsedStatement parse(Statement statement, QueryOptions defaultQueryOptions) { ParsedStatement parsedStatement = statementCache.getIfPresent(statement.getSql()); if (parsedStatement == null) { - parsedStatement = internalParse(statement, null); + parsedStatement = internalParse(statement, defaultQueryOptions); statementCache.put(statement.getSql(), parsedStatement.forCache()); return parsedStatement; } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementValueConverters.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementValueConverters.java index a477a664ec6..6be277777a6 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementValueConverters.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementValueConverters.java @@ -18,6 +18,7 @@ import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.Options.RpcPriority; +import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.TimestampBound; import com.google.cloud.spanner.TimestampBound.Mode; @@ -27,6 +28,7 @@ import com.google.common.base.Preconditions; import com.google.protobuf.Duration; import com.google.protobuf.util.Durations; +import com.google.spanner.v1.DirectedReadOptions; import com.google.spanner.v1.RequestOptions.Priority; import java.util.EnumSet; import java.util.HashMap; @@ -306,6 +308,48 @@ public TimestampBound convert(String value) { return null; } } + /** + * Converter from string to possible values for {@link com.google.spanner.v1.DirectedReadOptions}. + */ + static class DirectedReadOptionsConverter + implements ClientSideStatementValueConverter { + private final Pattern allowedValues; + + public DirectedReadOptionsConverter(String allowedValues) { + // Remove the single quotes at the beginning and end. + this.allowedValues = + Pattern.compile( + "(?is)\\A" + allowedValues.substring(1, allowedValues.length() - 1) + "\\z"); + } + + @Override + public Class getParameterClass() { + return DirectedReadOptions.class; + } + + @Override + public DirectedReadOptions convert(String value) { + Matcher matcher = allowedValues.matcher(value); + if (matcher.find()) { + try { + return DirectedReadOptionsUtil.parse(value); + } catch (SpannerException spannerException) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.INVALID_ARGUMENT, + String.format( + "Failed to parse '%s' as a valid value for DIRECTED_READ.\n" + + "The value should be a JSON string like this: '%s'.\n" + + "You can generate a valid JSON string from a DirectedReadOptions instance by calling %s.%s", + value, + "{\"includeReplicas\":{\"replicaSelections\":[{\"location\":\"eu-west1\",\"type\":\"READ_ONLY\"}]}}", + DirectedReadOptionsUtil.class.getName(), + "toString(DirectedReadOptions directedReadOptions)"), + spannerException); + } + } + return null; + } + } /** Converter for converting strings to {@link AutocommitDmlMode} values. */ static class AutocommitDmlModeConverter diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java index e58610b7d03..ffcef91d9c9 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java @@ -38,6 +38,7 @@ import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.TimestampBound; import com.google.cloud.spanner.connection.StatementResult.ResultType; +import com.google.spanner.v1.DirectedReadOptions; import com.google.spanner.v1.ExecuteBatchDmlRequest; import com.google.spanner.v1.ResultSetStats; import java.util.Iterator; @@ -489,6 +490,22 @@ default String getStatementTag() { */ TimestampBound getReadOnlyStaleness(); + /** + * Sets the {@link DirectedReadOptions} to use for both single-use and multi-use read-only + * transactions on this connection. + */ + default void setDirectedRead(DirectedReadOptions directedReadOptions) { + throw new UnsupportedOperationException("Unimplemented"); + } + + /** + * Returns the {@link DirectedReadOptions} that are used for both single-use and multi-use + * read-only transactions on this connection. + */ + default DirectedReadOptions getDirectedRead() { + throw new UnsupportedOperationException("Unimplemented"); + } + /** * Sets the query optimizer version to use for this connection. * diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java index 3e524e4fe92..e9aed66b6bb 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java @@ -53,6 +53,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.MoreExecutors; +import com.google.spanner.v1.DirectedReadOptions; import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions; import com.google.spanner.v1.ResultSetStats; import java.util.ArrayList; @@ -236,6 +237,7 @@ static UnitOfWorkType of(TransactionMode transactionMode) { */ private int maxPartitionedParallelism; + private DirectedReadOptions directedReadOptions = null; private QueryOptions queryOptions = QueryOptions.getDefaultInstance(); private RpcPriority rpcPriority = null; private SavepointSupport savepointSupport = SavepointSupport.FAIL_AFTER_ROLLBACK; @@ -510,6 +512,21 @@ public TimestampBound getReadOnlyStaleness() { return this.readOnlyStaleness; } + @Override + public void setDirectedRead(DirectedReadOptions directedReadOptions) { + ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG); + ConnectionPreconditions.checkState( + !isTransactionStarted(), + "Cannot set directed read options when a transaction has been started"); + this.directedReadOptions = directedReadOptions; + } + + @Override + public DirectedReadOptions getDirectedRead() { + ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG); + return this.directedReadOptions; + } + @Override public void setOptimizerVersion(String optimizerVersion) { Preconditions.checkNotNull(optimizerVersion); @@ -1131,7 +1148,8 @@ public ResultSet partitionQuery( CallType.SYNC, parsedStatement, getEffectivePartitionOptions(partitionOptions), - mergeDataBoost(mergeQueryRequestOptions(mergeQueryStatementTag(options))))); + mergeDataBoost( + mergeQueryRequestOptions(parsedStatement, mergeQueryStatementTag(options))))); } private PartitionOptions getEffectivePartitionOptions( @@ -1427,41 +1445,38 @@ private List parseUpdateStatements(Iterable updates) private QueryOption[] mergeDataBoost(QueryOption... options) { if (this.dataBoostEnabled) { - - // Shortcut for the most common scenario. - if (options == null || options.length == 0) { - options = new QueryOption[] {Options.dataBoostEnabled(true)}; - } else { - options = Arrays.copyOf(options, options.length + 1); - options[options.length - 1] = Options.dataBoostEnabled(true); - } + options = appendQueryOption(options, Options.dataBoostEnabled(true)); } return options; } private QueryOption[] mergeQueryStatementTag(QueryOption... options) { if (this.statementTag != null) { - // Shortcut for the most common scenario. - if (options == null || options.length == 0) { - options = new QueryOption[] {Options.tag(statementTag)}; - } else { - options = Arrays.copyOf(options, options.length + 1); - options[options.length - 1] = Options.tag(statementTag); - } + options = appendQueryOption(options, Options.tag(statementTag)); this.statementTag = null; } return options; } - private QueryOption[] mergeQueryRequestOptions(QueryOption... options) { + private QueryOption[] mergeQueryRequestOptions( + ParsedStatement parsedStatement, QueryOption... options) { if (this.rpcPriority != null) { - // Shortcut for the most common scenario. - if (options == null || options.length == 0) { - options = new QueryOption[] {Options.priority(this.rpcPriority)}; - } else { - options = Arrays.copyOf(options, options.length + 1); - options[options.length - 1] = Options.priority(this.rpcPriority); - } + options = appendQueryOption(options, Options.priority(this.rpcPriority)); + } + if (this.directedReadOptions != null + && currentUnitOfWork != null + && currentUnitOfWork.supportsDirectedReads(parsedStatement)) { + options = appendQueryOption(options, Options.directedRead(this.directedReadOptions)); + } + return options; + } + + private QueryOption[] appendQueryOption(QueryOption[] options, QueryOption append) { + if (options == null || options.length == 0) { + options = new QueryOption[] {append}; + } else { + options = Arrays.copyOf(options, options.length + 1); + options[options.length - 1] = append; } return options; } @@ -1516,7 +1531,7 @@ private ResultSet internalExecuteQuery( callType, statement, analyzeMode, - mergeQueryRequestOptions(mergeQueryStatementTag(options)))); + mergeQueryRequestOptions(statement, mergeQueryStatementTag(options)))); } private AsyncResultSet internalExecuteQueryAsync( @@ -1538,7 +1553,7 @@ private AsyncResultSet internalExecuteQueryAsync( callType, statement, analyzeMode, - mergeQueryRequestOptions(mergeQueryStatementTag(options))), + mergeQueryRequestOptions(statement, mergeQueryStatementTag(options))), spanner.getAsyncExecutorProvider(), options); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutor.java index eb317790976..3fbc3e7a8d4 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutor.java @@ -20,6 +20,7 @@ import com.google.cloud.spanner.TimestampBound; import com.google.cloud.spanner.connection.PgTransactionMode.IsolationLevel; import com.google.protobuf.Duration; +import com.google.spanner.v1.DirectedReadOptions; import com.google.spanner.v1.RequestOptions.Priority; /** @@ -65,6 +66,10 @@ interface ConnectionStatementExecutor { StatementResult statementShowReadOnlyStaleness(); + StatementResult statementSetDirectedRead(DirectedReadOptions directedReadOptions); + + StatementResult statementShowDirectedRead(); + StatementResult statementSetOptimizerVersion(String optimizerVersion); StatementResult statementShowOptimizerVersion(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorImpl.java index a3b4f92f145..0e8bdada223 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorImpl.java @@ -28,6 +28,7 @@ import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_DATA_BOOST_ENABLED; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_DEFAULT_TRANSACTION_ISOLATION; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_DELAY_TRANSACTION_START_UNTIL_FIRST_WRITE; +import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_DIRECTED_READ; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_MAX_PARTITIONED_PARALLELISM; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_MAX_PARTITIONS; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_OPTIMIZER_STATISTICS_PACKAGE; @@ -49,6 +50,7 @@ import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_COMMIT_TIMESTAMP; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_DATA_BOOST_ENABLED; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_DELAY_TRANSACTION_START_UNTIL_FIRST_WRITE; +import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_DIRECTED_READ; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_MAX_PARTITIONED_PARALLELISM; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_MAX_PARTITIONS; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_OPTIMIZER_STATISTICS_PACKAGE; @@ -91,6 +93,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.protobuf.Duration; +import com.google.spanner.v1.DirectedReadOptions; import com.google.spanner.v1.PlanNode; import com.google.spanner.v1.QueryPlan; import com.google.spanner.v1.RequestOptions; @@ -283,6 +286,21 @@ public StatementResult statementShowReadOnlyStaleness() { SHOW_READ_ONLY_STALENESS); } + @Override + public StatementResult statementSetDirectedRead(DirectedReadOptions directedReadOptions) { + getConnection().setDirectedRead(directedReadOptions); + return noResult(SET_DIRECTED_READ); + } + + @Override + public StatementResult statementShowDirectedRead() { + DirectedReadOptions directedReadOptions = getConnection().getDirectedRead(); + return resultSet( + String.format("%sDIRECTED_READ", getNamespace(connection.getDialect())), + DirectedReadOptionsUtil.toString(directedReadOptions), + SHOW_DIRECTED_READ); + } + @Override public StatementResult statementSetOptimizerVersion(String optimizerVersion) { getConnection().setOptimizerVersion(optimizerVersion); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectedReadOptionsUtil.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectedReadOptionsUtil.java new file mode 100644 index 00000000000..8b1f8a90199 --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectedReadOptionsUtil.java @@ -0,0 +1,55 @@ +/* + * Copyright 2024 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.connection; + +import com.google.cloud.spanner.SpannerExceptionFactory; +import com.google.common.base.Strings; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; +import com.google.spanner.v1.DirectedReadOptions; + +public class DirectedReadOptionsUtil { + + /** + * Generates a valid JSON string for the given {@link DirectedReadOptions} that can be used with + * the JDBC driver. + */ + public static String toString(DirectedReadOptions directedReadOptions) { + if (directedReadOptions == null + || DirectedReadOptions.getDefaultInstance().equals(directedReadOptions)) { + return ""; + } + try { + return JsonFormat.printer().omittingInsignificantWhitespace().print(directedReadOptions); + } catch (InvalidProtocolBufferException invalidProtocolBufferException) { + throw SpannerExceptionFactory.asSpannerException(invalidProtocolBufferException); + } + } + + static DirectedReadOptions parse(String json) { + if (Strings.isNullOrEmpty(json)) { + return DirectedReadOptions.getDefaultInstance(); + } + DirectedReadOptions.Builder builder = DirectedReadOptions.newBuilder(); + try { + JsonFormat.parser().merge(json, builder); + return builder.build(); + } catch (InvalidProtocolBufferException invalidProtocolBufferException) { + throw SpannerExceptionFactory.asSpannerException(invalidProtocolBufferException); + } + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReadOnlyTransaction.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReadOnlyTransaction.java index 5a005ff8052..63e5221362e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReadOnlyTransaction.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReadOnlyTransaction.java @@ -107,6 +107,11 @@ public boolean isReadOnly() { return true; } + @Override + public boolean supportsDirectedReads(ParsedStatement ignore) { + return true; + } + @Override void checkAborted() { // No-op for read-only transactions as they cannot abort. diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java index 52486ed43e1..56ddc16322a 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java @@ -188,6 +188,11 @@ public boolean isReadOnly() { return readOnly; } + @Override + public boolean supportsDirectedReads(ParsedStatement parsedStatement) { + return parsedStatement.isQuery(); + } + private void checkAndMarkUsed() { Preconditions.checkState(!used, "This single-use transaction has already been used"); used = true; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResult.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResult.java index 1452d906a96..d180423266e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResult.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResult.java @@ -63,6 +63,8 @@ enum ClientSideStatementType { SHOW_COMMIT_RESPONSE, SHOW_READ_ONLY_STALENESS, SET_READ_ONLY_STALENESS, + SHOW_DIRECTED_READ, + SET_DIRECTED_READ, SHOW_OPTIMIZER_VERSION, SET_OPTIMIZER_VERSION, SHOW_OPTIMIZER_STATISTICS_PACKAGE, diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/UnitOfWork.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/UnitOfWork.java index ea315e94c0e..1c8bc6a29c0 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/UnitOfWork.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/UnitOfWork.java @@ -128,6 +128,14 @@ public boolean isActive() { /** @return true if this unit of work is read-only. */ boolean isReadOnly(); + /** + * @return true if this unit of work supports {@link + * com.google.spanner.v1.DirectedReadOptions} + */ + default boolean supportsDirectedReads(ParsedStatement parsedStatement) { + return false; + } + /** * Executes a query with the given options. If {@link AnalyzeMode} is set to {@link * AnalyzeMode#PLAN} or {@link AnalyzeMode#PROFILE}, the returned {@link ResultSet} will include diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java index a8618af5c79..c9aa5987663 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java @@ -339,7 +339,7 @@ public GapicSpannerRpc(final SpannerOptions options) { SpannerInterceptorProvider.create( MoreObjects.firstNonNull( options.getInterceptorProvider(), - SpannerInterceptorProvider.createDefault())) + SpannerInterceptorProvider.createDefault(options.getOpenTelemetry()))) // This sets the response compressor (Server -> Client). .withEncoding(compressorName)) .setHeaderProvider(headerProviderWithUserAgent) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/HeaderInterceptor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/HeaderInterceptor.java index d4725b25427..7de63dc33ba 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/HeaderInterceptor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/HeaderInterceptor.java @@ -22,6 +22,7 @@ import static com.google.cloud.spanner.spi.v1.SpannerRpcViews.SPANNER_GFE_HEADER_MISSING_COUNT; import static com.google.cloud.spanner.spi.v1.SpannerRpcViews.SPANNER_GFE_LATENCY; +import com.google.cloud.spanner.SpannerRpcMetrics; import io.grpc.CallOptions; import io.grpc.Channel; import io.grpc.ClientCall; @@ -37,6 +38,8 @@ import io.opencensus.tags.TagValue; import io.opencensus.tags.Tagger; import io.opencensus.tags.Tags; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -63,8 +66,23 @@ class HeaderInterceptor implements ClientInterceptor { private static final Logger LOGGER = Logger.getLogger(HeaderInterceptor.class.getName()); private static final Level LEVEL = Level.INFO; + private final SpannerRpcMetrics spannerRpcMetrics; - HeaderInterceptor() {} + HeaderInterceptor(SpannerRpcMetrics spannerRpcMetrics) { + this.spannerRpcMetrics = spannerRpcMetrics; + } + + private class SpannerProperties { + String projectId; + String instanceId; + String databaseId; + + SpannerProperties(String projectId, String instanceId, String databaseId) { + this.databaseId = databaseId; + this.instanceId = instanceId; + this.projectId = projectId; + } + } @Override public ClientCall interceptCall( @@ -72,13 +90,14 @@ public ClientCall interceptCall( return new SimpleForwardingClientCall(next.newCall(method, callOptions)) { @Override public void start(Listener responseListener, Metadata headers) { - TagContext tagContext = getTagContext(headers, method.getFullMethodName()); + SpannerProperties spannerProperties = createProjectPropertes(headers); + TagContext tagContext = getTagContext(method.getFullMethodName(), spannerProperties); + Attributes attributes = getMetricAttributes(method.getFullMethodName(), spannerProperties); super.start( new SimpleForwardingClientCallListener(responseListener) { @Override public void onHeaders(Metadata metadata) { - - processHeader(metadata, tagContext); + processHeader(metadata, tagContext, attributes); super.onHeaders(metadata); } }, @@ -87,7 +106,7 @@ public void onHeaders(Metadata metadata) { }; } - private void processHeader(Metadata metadata, TagContext tagContext) { + private void processHeader(Metadata metadata, TagContext tagContext, Attributes attributes) { MeasureMap measureMap = STATS_RECORDER.newMeasureMap(); if (metadata.get(SERVER_TIMING_HEADER_KEY) != null) { String serverTiming = metadata.get(SERVER_TIMING_HEADER_KEY); @@ -98,27 +117,20 @@ private void processHeader(Metadata metadata, TagContext tagContext) { measureMap.put(SPANNER_GFE_LATENCY, latency); measureMap.put(SPANNER_GFE_HEADER_MISSING_COUNT, 0L); measureMap.record(tagContext); + + spannerRpcMetrics.recordGfeLatency(latency, attributes); + spannerRpcMetrics.recordGfeHeaderMissingCount(0L, attributes); } catch (NumberFormatException e) { LOGGER.log(LEVEL, "Invalid server-timing object in header", matcher.group("dur")); } } } else { + spannerRpcMetrics.recordGfeHeaderMissingCount(1L, attributes); measureMap.put(SPANNER_GFE_HEADER_MISSING_COUNT, 1L).record(tagContext); } } - private TagContext getTagContext( - String method, String projectId, String instanceId, String databaseId) { - return TAGGER - .currentBuilder() - .putLocal(PROJECT_ID, TagValue.create(projectId)) - .putLocal(INSTANCE_ID, TagValue.create(instanceId)) - .putLocal(DATABASE_ID, TagValue.create(databaseId)) - .putLocal(METHOD, TagValue.create(method)) - .build(); - } - - private TagContext getTagContext(Metadata headers, String method) { + private SpannerProperties createProjectPropertes(Metadata headers) { String projectId = "undefined-project"; String instanceId = "undefined-database"; String databaseId = "undefined-database"; @@ -137,6 +149,26 @@ private TagContext getTagContext(Metadata headers, String method) { LOGGER.log(LEVEL, "Error parsing google cloud resource header: " + googleResourcePrefix); } } - return getTagContext(method, projectId, instanceId, databaseId); + return new SpannerProperties(projectId, instanceId, databaseId); + } + + private TagContext getTagContext(String method, SpannerProperties spannerProperties) { + return TAGGER + .currentBuilder() + .putLocal(PROJECT_ID, TagValue.create(spannerProperties.projectId)) + .putLocal(INSTANCE_ID, TagValue.create(spannerProperties.instanceId)) + .putLocal(DATABASE_ID, TagValue.create(spannerProperties.databaseId)) + .putLocal(METHOD, TagValue.create(method)) + .build(); + } + + private Attributes getMetricAttributes(String method, SpannerProperties spannerProperties) { + AttributesBuilder attributesBuilder = Attributes.builder(); + attributesBuilder.put("database", spannerProperties.databaseId); + attributesBuilder.put("instance_id", spannerProperties.instanceId); + attributesBuilder.put("project_id", spannerProperties.projectId); + attributesBuilder.put("method", method); + + return attributesBuilder.build(); } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerInterceptorProvider.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerInterceptorProvider.java index e45702de8df..9b1a2fd3c1f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerInterceptorProvider.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerInterceptorProvider.java @@ -16,9 +16,14 @@ package com.google.cloud.spanner.spi.v1; import com.google.api.core.InternalApi; +import com.google.api.core.ObsoleteApi; import com.google.api.gax.grpc.GrpcInterceptorProvider; +import com.google.cloud.spanner.SpannerRpcMetrics; import com.google.common.collect.ImmutableList; import io.grpc.ClientInterceptor; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -29,21 +34,24 @@ */ @InternalApi("Exposed for testing") public class SpannerInterceptorProvider implements GrpcInterceptorProvider { - - private static final List defaultInterceptors = - ImmutableList.of( - new SpannerErrorInterceptor(), - new LoggingInterceptor(Logger.getLogger(GapicSpannerRpc.class.getName()), Level.FINER), - new HeaderInterceptor()); - private final List clientInterceptors; private SpannerInterceptorProvider(List clientInterceptors) { this.clientInterceptors = clientInterceptors; } + @ObsoleteApi("This method always uses Global OpenTelemetry") public static SpannerInterceptorProvider createDefault() { - return new SpannerInterceptorProvider(defaultInterceptors); + return createDefault(GlobalOpenTelemetry.get()); + } + + public static SpannerInterceptorProvider createDefault(OpenTelemetry openTelemetry) { + List defaultInterceptorList = new ArrayList<>(); + defaultInterceptorList.add(new SpannerErrorInterceptor()); + defaultInterceptorList.add( + new LoggingInterceptor(Logger.getLogger(GapicSpannerRpc.class.getName()), Level.FINER)); + defaultInterceptorList.add(new HeaderInterceptor(new SpannerRpcMetrics(openTelemetry))); + return new SpannerInterceptorProvider(ImmutableList.copyOf(defaultInterceptorList)); } static SpannerInterceptorProvider create(GrpcInterceptorProvider provider) { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerRpcViews.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerRpcViews.java index b1b68e95d1a..7d6cc163b46 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerRpcViews.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerRpcViews.java @@ -15,6 +15,8 @@ */ package com.google.cloud.spanner.spi.v1; +import com.google.api.core.ObsoleteApi; +import com.google.cloud.spanner.SpannerOptions; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import io.opencensus.stats.Aggregation; @@ -91,28 +93,49 @@ public class SpannerRpcViews { * measures the latency between Google's network receives an RPC and reads back the first byte of * the response. gfe_header_missing_count is a counter of the number of RPC responses without a * server-timing header. + * + * @deprecated The OpenCensus project is deprecated. Use OpenTelemetry to get gfe_latency and + * gfe_header_missing_count metrics. */ @VisibleForTesting + @ObsoleteApi( + "The OpenCensus project is deprecated. Use OpenTelemetry to get gfe_latency and gfe_header_missing_count metrics.") public static void registerGfeLatencyAndHeaderMissingCountViews() { - viewManager.registerView(SPANNER_GFE_LATENCY_VIEW); - viewManager.registerView(SPANNER_GFE_HEADER_MISSING_COUNT_VIEW); + if (SpannerOptions.isEnabledOpenCensusMetrics()) { + viewManager.registerView(SPANNER_GFE_LATENCY_VIEW); + viewManager.registerView(SPANNER_GFE_HEADER_MISSING_COUNT_VIEW); + } } /** * Register GFE Latency view. gfe_latency measures the latency between Google's network receives * an RPC and reads back the first byte of the response. + * + * @deprecated The OpenCensus project is deprecated. Use OpenTelemetry to get gfe_latency and + * gfe_header_missing_count metrics. */ @VisibleForTesting + @ObsoleteApi( + "The OpenCensus project is deprecated. Use OpenTelemetry to get gfe_latency and gfe_header_missing_count metrics.") public static void registerGfeLatencyView() { - viewManager.registerView(SPANNER_GFE_LATENCY_VIEW); + if (SpannerOptions.isEnabledOpenCensusMetrics()) { + viewManager.registerView(SPANNER_GFE_LATENCY_VIEW); + } } /** * Register GFE Header Missing Count view. gfe_header_missing_count is a counter of the number of * RPC responses without a server-timing header. + * + * @deprecated The OpenCensus project is deprecated. Use OpenTelemetry to get gfe_latency and + * gfe_header_missing_count metrics. */ @VisibleForTesting + @ObsoleteApi( + "The OpenCensus project is deprecated. Use OpenTelemetry to get gfe_latency and gfe_header_missing_count metrics.") public static void registerGfeHeaderMissingCountView() { - viewManager.registerView(SPANNER_GFE_HEADER_MISSING_COUNT_VIEW); + if (SpannerOptions.isEnabledOpenCensusMetrics()) { + viewManager.registerView(SPANNER_GFE_HEADER_MISSING_COUNT_VIEW); + } } } 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 4b9b82240c4..e2001364abb 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 @@ -16,12 +16,15 @@ package com.google.cloud.spanner.testing; +import com.google.api.client.util.BackOff; +import com.google.api.client.util.ExponentialBackOff; import com.google.api.gax.longrunning.OperationFuture; import com.google.cloud.spanner.BatchClient; import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.InstanceId; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerException; @@ -34,6 +37,7 @@ import java.util.Collections; import java.util.List; import java.util.Random; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; @@ -54,7 +58,7 @@ public class RemoteSpannerHelper { private static int dbRolePrefix = new Random().nextInt(Integer.MAX_VALUE); private static final AtomicInteger backupSeq = new AtomicInteger(); private static final int backupPrefix = new Random().nextInt(Integer.MAX_VALUE); - private final List dbs = new ArrayList<>(); + private final List databaseIds = new ArrayList<>(); protected RemoteSpannerHelper(SpannerOptions options, InstanceId instanceId, Spanner client) { this.options = options; @@ -132,19 +136,47 @@ public String getUniqueBackupId() { public Database createTestDatabase(Dialect dialect, Iterable statements) throws SpannerException { String dbId = getUniqueDatabaseId(); + DatabaseId databaseId = DatabaseId.of(instanceId.getProject(), instanceId.getInstance(), dbId); Database databaseToCreate = - client - .getDatabaseAdminClient() - .newDatabaseBuilder( - DatabaseId.of(instanceId.getProject(), instanceId.getInstance(), dbId)) - .setDialect(dialect) - .build(); + client.getDatabaseAdminClient().newDatabaseBuilder(databaseId).setDialect(dialect).build(); try { Iterable ddlStatements = dialect == Dialect.POSTGRESQL ? Collections.emptyList() : statements; - OperationFuture op = - client.getDatabaseAdminClient().createDatabase(databaseToCreate, ddlStatements); - Database db = op.get(); + Database db = null; + final int maxAttempts = 20; + BackOff backOff = + new ExponentialBackOff.Builder() + .setInitialIntervalMillis(10_000) + .setMaxIntervalMillis(60_000) + .setMaxElapsedTimeMillis(120_000) + .build(); + for (int attempts = 0; attempts < maxAttempts; attempts++) { + try { + OperationFuture op = + client.getDatabaseAdminClient().createDatabase(databaseToCreate, ddlStatements); + db = op.get(); + break; + } catch (ExecutionException executionException) { + SpannerException spannerException = + SpannerExceptionFactory.asSpannerException(executionException.getCause()); + if (spannerException.getErrorCode() != ErrorCode.RESOURCE_EXHAUSTED) { + throw executionException; + } + } catch (SpannerException spannerException) { + if (spannerException.getErrorCode() != ErrorCode.RESOURCE_EXHAUSTED) { + throw spannerException; + } + } + long sleep = backOff.nextBackOffMillis(); + if (sleep > 0L) { + Thread.sleep(sleep); + } + } + if (db == null) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.RESOURCE_EXHAUSTED, + String.format("Failed to create test database after %d attempts", maxAttempts)); + } if (dialect == Dialect.POSTGRESQL && Iterables.size(statements) > 0) { client .getDatabaseAdminClient() @@ -152,10 +184,11 @@ public Database createTestDatabase(Dialect dialect, Iterable statements) .get(); } logger.log(Level.FINE, "Created test database {0}", db.getId()); - dbs.add(db); return db; } catch (Exception e) { throw SpannerExceptionFactory.newSpannerException(e); + } finally { + databaseIds.add(databaseId); } } @@ -167,13 +200,15 @@ public Database createTestDatabase(Iterable statements) throws SpannerEx public void cleanUp() { // Drop all the databases we created explicitly. int numDropped = 0; - for (Database db : dbs) { + for (DatabaseId databaseId : databaseIds) { try { - logger.log(Level.INFO, "Dropping test database {0}", db.getId()); - db.drop(); + logger.log(Level.INFO, "Dropping test database {0}", databaseId); + client + .getDatabaseAdminClient() + .dropDatabase(databaseId.getInstanceId().getInstance(), databaseId.getDatabase()); ++numDropped; - } catch (SpannerException e) { - logger.log(Level.SEVERE, "Failed to drop test database " + db.getId(), e); + } catch (Throwable e) { + logger.log(Level.SEVERE, "Failed to drop test database " + databaseId, e); } } logger.log(Level.INFO, "Dropped {0} test database(s)", numDropped); diff --git a/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json b/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json index 576991b8e02..20d7522961d 100644 --- a/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json +++ b/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json @@ -76,6 +76,15 @@ "method": "statementShowReadOnlyStaleness", "exampleStatements": ["show variable read_only_staleness"] }, + { + "name": "SHOW VARIABLE DIRECTED_READ", + "executorName": "ClientSideStatementNoParamExecutor", + "resultType": "RESULT_SET", + "statementType": "SHOW_DIRECTED_READ", + "regex": "(?is)\\A\\s*show\\s+variable\\s+directed_read\\s*\\z", + "method": "statementShowDirectedRead", + "exampleStatements": ["show variable directed_read"] + }, { "name": "SHOW VARIABLE OPTIMIZER_VERSION", "executorName": "ClientSideStatementNoParamExecutor", @@ -373,6 +382,21 @@ "converterName": "ClientSideStatementValueConverters$ReadOnlyStalenessConverter" } }, + { + "name": "SET DIRECTED_READ = ''|''", + "executorName": "ClientSideStatementSetExecutor", + "resultType": "NO_RESULT", + "statementType": "SET_DIRECTED_READ", + "regex": "(?is)\\A\\s*set\\s+directed_read\\s*(?:=)\\s*(.*)\\z", + "method": "statementSetDirectedRead", + "exampleStatements": ["set directed_read='{\"includeReplicas\":{\"replicaSelections\":[{\"location\":\"eu-west1\",\"type\":\"READ_ONLY\"}]}}'", "set directed_read=''"], + "setStatement": { + "propertyName": "DIRECTED_READ", + "separator": "=", + "allowedValues": "'((\\S+)|())'", + "converterName": "ClientSideStatementValueConverters$DirectedReadOptionsConverter" + } + }, { "name": "SET OPTIMIZER_VERSION = ''|'LATEST'|''", "executorName": "ClientSideStatementSetExecutor", diff --git a/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/PG_ClientSideStatements.json b/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/PG_ClientSideStatements.json index cb601d7277c..bd04b725c29 100644 --- a/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/PG_ClientSideStatements.json +++ b/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/PG_ClientSideStatements.json @@ -76,6 +76,15 @@ "method": "statementShowReadOnlyStaleness", "exampleStatements": ["show spanner.read_only_staleness","show variable spanner.read_only_staleness"] }, + { + "name": "SHOW [VARIABLE] SPANNER.DIRECTED_READ", + "executorName": "ClientSideStatementNoParamExecutor", + "resultType": "RESULT_SET", + "statementType": "SHOW_DIRECTED_READ", + "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?spanner\\.directed_read\\s*\\z", + "method": "statementShowDirectedRead", + "exampleStatements": ["show spanner.directed_read", "show variable spanner.directed_read"] + }, { "name": "SHOW [VARIABLE] SPANNER.OPTIMIZER_VERSION", "executorName": "ClientSideStatementNoParamExecutor", @@ -525,6 +534,21 @@ "converterName": "ClientSideStatementValueConverters$ReadOnlyStalenessConverter" } }, + { + "name": "SET SPANNER.DIRECTED_READ =|TO ''|''", + "executorName": "ClientSideStatementSetExecutor", + "resultType": "NO_RESULT", + "statementType": "SET_DIRECTED_READ", + "regex": "(?is)\\A\\s*set\\s+spanner\\.directed_read(?:\\s*=\\s*|\\s+to\\s+)(.*)\\z", + "method": "statementSetDirectedRead", + "exampleStatements": ["set spanner.directed_read='{\"includeReplicas\":{\"replicaSelections\":[{\"location\":\"eu-west1\",\"type\":\"READ_ONLY\"}]}}'", "set spanner.directed_read=''"], + "setStatement": { + "propertyName": "SPANNER.DIRECTED_READ", + "separator": "(?:=|\\s+TO\\s+)", + "allowedValues": "'((\\S+)|())'", + "converterName": "ClientSideStatementValueConverters$DirectedReadOptionsConverter" + } + }, { "name": "SET SPANNER.OPTIMIZER_VERSION =|TO ''|'LATEST'|''", "executorName": "ClientSideStatementSetExecutor", diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java index 84f7fb0db9b..08d22dd2d67 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java @@ -22,7 +22,8 @@ import com.google.api.core.ApiFutures; import com.google.cloud.Timestamp; -import io.opencensus.trace.Span; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Scope; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -36,8 +37,11 @@ public class AsyncTransactionManagerImplTest { @Test public void testCommitReturnsCommitStats() { + Span oTspan = mock(Span.class); + ISpan span = new OpenTelemetrySpan(oTspan); + when(oTspan.makeCurrent()).thenReturn(mock(Scope.class)); try (AsyncTransactionManagerImpl manager = - new AsyncTransactionManagerImpl(session, mock(Span.class), Options.commitStats())) { + new AsyncTransactionManagerImpl(session, span, Options.commitStats())) { when(session.newTransaction(Options.fromTransactionOptions(Options.commitStats()))) .thenReturn(transaction); when(transaction.ensureTxnAsync()).thenReturn(ApiFutures.immediateFuture(null)); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BatchClientImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BatchClientImplTest.java index 18ae8a07b35..b7c4834044a 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BatchClientImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BatchClientImplTest.java @@ -35,9 +35,11 @@ import com.google.protobuf.util.Timestamps; import com.google.spanner.v1.Session; import com.google.spanner.v1.Transaction; +import io.opentelemetry.api.OpenTelemetry; import java.util.Collections; import java.util.Map; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -62,6 +64,12 @@ public final class BatchClientImplTest { private BatchClient client; + @BeforeClass + public static void setupOpenTelemetry() { + SpannerOptions.resetActiveTracingFramework(); + SpannerOptions.enableOpenTelemetryTraces(); + } + @SuppressWarnings("unchecked") @Before public void setUp() { @@ -74,6 +82,7 @@ public void setUp() { when(spannerOptions.getClock()).thenReturn(NanoClock.getDefaultClock()); when(spannerOptions.getSpannerRpcV1()).thenReturn(gapicRpc); when(spannerOptions.getSessionLabels()).thenReturn(Collections.emptyMap()); + when(spannerOptions.getOpenTelemetry()).thenReturn(OpenTelemetry.noop()); GrpcTransportOptions transportOptions = mock(GrpcTransportOptions.class); when(transportOptions.getExecutorFactory()).thenReturn(mock(ExecutorFactory.class)); when(spannerOptions.getTransportOptions()).thenReturn(transportOptions); 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 2c2d5a1a139..ccbf3c0b2b9 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 @@ -102,6 +102,8 @@ import io.grpc.StatusRuntimeException; import io.grpc.inprocess.InProcessServerBuilder; import io.grpc.protobuf.lite.ProtoLiteUtils; +import io.opencensus.trace.Tracing; +import io.opentelemetry.api.OpenTelemetry; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -133,6 +135,7 @@ @RunWith(JUnit4.class) public class DatabaseClientImplTest { + private static final String TEST_PROJECT = "my-project"; private static final String TEST_INSTANCE = "my-instance"; private static final String TEST_DATABASE = "my-database"; @@ -3374,7 +3377,10 @@ public void testReadWriteTransaction_usesOptions() { when(pool.getSession()).thenReturn(session); TransactionOption option = mock(TransactionOption.class); - DatabaseClientImpl client = new DatabaseClientImpl(pool); + TraceWrapper traceWrapper = + new TraceWrapper(Tracing.getTracer(), OpenTelemetry.noop().getTracer("")); + + DatabaseClientImpl client = new DatabaseClientImpl(pool, traceWrapper); client.readWriteTransaction(option); verify(session).readWriteTransaction(option); @@ -3387,7 +3393,7 @@ public void testTransactionManager_usesOptions() { when(pool.getSession()).thenReturn(session); TransactionOption option = mock(TransactionOption.class); - DatabaseClientImpl client = new DatabaseClientImpl(pool); + DatabaseClientImpl client = new DatabaseClientImpl(pool, mock(TraceWrapper.class)); try (TransactionManager ignore = client.transactionManager(option)) { verify(session).transactionManager(option); } @@ -3400,7 +3406,7 @@ public void testRunAsync_usesOptions() { when(pool.getSession()).thenReturn(session); TransactionOption option = mock(TransactionOption.class); - DatabaseClientImpl client = new DatabaseClientImpl(pool); + DatabaseClientImpl client = new DatabaseClientImpl(pool, mock(TraceWrapper.class)); client.runAsync(option); verify(session).runAsync(option); @@ -3413,7 +3419,7 @@ public void testTransactionManagerAsync_usesOptions() { when(pool.getSession()).thenReturn(session); TransactionOption option = mock(TransactionOption.class); - DatabaseClientImpl client = new DatabaseClientImpl(pool); + DatabaseClientImpl client = new DatabaseClientImpl(pool, mock(TraceWrapper.class)); try (AsyncTransactionManager ignore = client.transactionManagerAsync(option)) { verify(session).transactionManagerAsync(option); } @@ -3635,6 +3641,112 @@ public void testAsyncTransactionManagerCommitWithPriority() { assertEquals(Priority.PRIORITY_HIGH, request.getRequestOptions().getPriority()); } + @Test + public void testCommitWithoutMaxCommitDelay() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + TransactionRunner runner = client.readWriteTransaction(); + runner.run( + transaction -> { + transaction.buffer(Mutation.delete("TEST", KeySet.all())); + return null; + }); + + List requests = mockSpanner.getRequestsOfType(CommitRequest.class); + assertThat(requests).hasSize(1); + CommitRequest request = requests.get(0); + assertFalse(request.hasMaxCommitDelay()); + } + + @Test + public void testCommitWithMaxCommitDelay() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + TransactionRunner runner = + client.readWriteTransaction(Options.maxCommitDelay(java.time.Duration.ofMillis(100))); + runner.run( + transaction -> { + transaction.buffer(Mutation.delete("TEST", KeySet.all())); + return null; + }); + + List requests = mockSpanner.getRequestsOfType(CommitRequest.class); + assertThat(requests).hasSize(1); + CommitRequest request = requests.get(0); + assertNotNull(request.getMaxCommitDelay()); + assertEquals( + com.google.protobuf.Duration.newBuilder().setNanos(100000000).build(), + request.getMaxCommitDelay()); + } + + @Test + public void testTransactionManagerCommitWithMaxCommitDelay() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + TransactionManager manager = + client.transactionManager(Options.maxCommitDelay(java.time.Duration.ofMillis(100))); + TransactionContext transaction = manager.begin(); + transaction.buffer(Mutation.delete("TEST", KeySet.all())); + manager.commit(); + + List requests = mockSpanner.getRequestsOfType(CommitRequest.class); + assertThat(requests).hasSize(1); + CommitRequest request = requests.get(0); + assertNotNull(request.getMaxCommitDelay()); + assertEquals( + com.google.protobuf.Duration.newBuilder().setNanos(100000000).build(), + request.getMaxCommitDelay()); + } + + @Test + public void testAsyncRunnerCommitWithMaxCommitDelay() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + AsyncRunner runner = client.runAsync(Options.maxCommitDelay(java.time.Duration.ofMillis(100))); + get( + runner.runAsync( + txn -> { + txn.buffer(Mutation.delete("TEST", KeySet.all())); + return ApiFutures.immediateFuture(null); + }, + executor)); + + List requests = mockSpanner.getRequestsOfType(CommitRequest.class); + assertThat(requests).hasSize(1); + CommitRequest request = requests.get(0); + assertNotNull(request.getMaxCommitDelay()); + assertEquals( + com.google.protobuf.Duration.newBuilder().setNanos(100000000).build(), + request.getMaxCommitDelay()); + } + + @Test + public void testAsyncTransactionManagerCommitWithMaxCommitDelay() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + try (AsyncTransactionManager manager = + client.transactionManagerAsync(Options.maxCommitDelay(java.time.Duration.ofMillis(100)))) { + TransactionContextFuture transaction = manager.beginAsync(); + get( + transaction + .then( + (txn, input) -> { + txn.buffer(Mutation.delete("TEST", KeySet.all())); + return ApiFutures.immediateFuture(null); + }, + executor) + .commitAsync()); + } + + List requests = mockSpanner.getRequestsOfType(CommitRequest.class); + assertThat(requests).hasSize(1); + CommitRequest request = requests.get(0); + assertNotNull(request.getMaxCommitDelay()); + assertEquals( + com.google.protobuf.Duration.newBuilder().setNanos(100000000).build(), + request.getMaxCommitDelay()); + } + @Test public void singleUseNoAction_ClearsCheckedOutSession() { DatabaseClientImpl client = diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/FailOnOverkillTraceComponentImpl.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/FailOnOverkillTraceComponentImpl.java index 4877c60837f..4d9b4ddd805 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/FailOnOverkillTraceComponentImpl.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/FailOnOverkillTraceComponentImpl.java @@ -29,6 +29,7 @@ import io.opencensus.trace.SpanBuilder; import io.opencensus.trace.SpanContext; import io.opencensus.trace.SpanId; +import io.opencensus.trace.Status; import io.opencensus.trace.TraceComponent; import io.opencensus.trace.TraceId; import io.opencensus.trace.TraceOptions; @@ -43,6 +44,7 @@ import io.opencensus.trace.propagation.BinaryFormat; import io.opencensus.trace.propagation.PropagationComponent; import io.opencensus.trace.propagation.TextFormat; +import java.util.ArrayList; import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.List; @@ -62,6 +64,8 @@ public class FailOnOverkillTraceComponentImpl extends TraceComponent { private final TraceConfig traceConfig = new TestTraceConfig(); private static final Map spans = new LinkedHashMap<>(); + private static final List annotations = new ArrayList<>(); + public static class TestSpan extends Span { @GuardedBy("this") private volatile boolean ended = false; @@ -75,14 +79,27 @@ private TestSpan(String spanName, SpanContext context, EnumSet options) } @Override - public void addAnnotation(String description, Map attributes) {} + public void addAnnotation(String description, Map attributes) { + annotations.add(description); + } + + @Override + public void addAnnotation(Annotation annotation) { + annotations.add(annotation.getDescription()); + } + + @Override + public void putAttributes(Map attributes) {} @Override - public void addAnnotation(Annotation annotation) {} + public void addAttributes(Map attributes) {} @Override public void addLink(Link link) {} + @Override + public void setStatus(Status status) {} + @Override public void end(EndSpanOptions options) { synchronized (this) { @@ -210,10 +227,18 @@ Map getSpans() { return spans; } + List getAnnotations() { + return annotations; + } + void clearSpans() { spans.clear(); } + void clearAnnotations() { + annotations.clear(); + } + @Override public PropagationComponent getPropagationComponent() { return propagationComponent; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITSessionPoolIntegrationTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITSessionPoolIntegrationTest.java index 36b9bee0af7..be9f6841f20 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITSessionPoolIntegrationTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ITSessionPoolIntegrationTest.java @@ -20,6 +20,8 @@ import com.google.cloud.grpc.GrpcTransportOptions.ExecutorFactory; import com.google.cloud.spanner.SessionPool.PooledSessionFuture; +import io.opencensus.trace.Tracing; +import io.opentelemetry.api.OpenTelemetry; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -93,7 +95,9 @@ public ScheduledExecutorService get() { return new ScheduledThreadPoolExecutor(2); } }, - ((SpannerImpl) env.getTestHelper().getClient()).getSessionClient(db.getId())); + ((SpannerImpl) env.getTestHelper().getClient()).getSessionClient(db.getId()), + new TraceWrapper(Tracing.getTracer(), OpenTelemetry.noop().getTracer("")), + OpenTelemetry.noop()); } @Test diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java index 2c59439ce7d..4d4f639d3d9 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java @@ -20,8 +20,8 @@ import com.google.api.client.util.ExponentialBackOff; import com.google.api.gax.longrunning.OperationFuture; -import com.google.api.gax.paging.Page; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.DatabaseInfo.DatabaseField; import com.google.cloud.spanner.testing.EmulatorSpannerHelper; import com.google.cloud.spanner.testing.RemoteSpannerHelper; import com.google.common.collect.Iterators; @@ -210,30 +210,32 @@ static boolean isRetryableResourceExhaustedException(SpannerException exception) } private void cleanUpOldDatabases(InstanceId instanceId) { - long OLD_DB_THRESHOLD_SECS = TimeUnit.SECONDS.convert(24L, TimeUnit.HOURS); + long OLD_DB_THRESHOLD_SECS = TimeUnit.SECONDS.convert(6L, TimeUnit.HOURS); Timestamp currentTimestamp = Timestamp.now(); int numDropped = 0; - Page page = databaseAdminClient.listDatabases(instanceId.getInstance()); String TEST_DB_REGEX = "(testdb_(.*)_(.*))|(mysample-(.*))"; logger.log(Level.INFO, "Dropping old test databases from {0}", instanceId.getName()); - while (page != null) { - for (Database db : page.iterateAll()) { - try { - long timeDiff = currentTimestamp.getSeconds() - db.getCreateTime().getSeconds(); - // Delete all databases which are more than OLD_DB_THRESHOLD_SECS seconds - // old. - if ((db.getId().getDatabase().matches(TEST_DB_REGEX)) - && (timeDiff > OLD_DB_THRESHOLD_SECS)) { - logger.log(Level.INFO, "Dropping test database {0}", db.getId()); - db.drop(); - ++numDropped; + for (Database db : databaseAdminClient.listDatabases(instanceId.getInstance()).iterateAll()) { + try { + long timeDiff = currentTimestamp.getSeconds() - db.getCreateTime().getSeconds(); + // Delete all databases which are more than OLD_DB_THRESHOLD_SECS seconds old. + if ((db.getId().getDatabase().matches(TEST_DB_REGEX)) + && (timeDiff > OLD_DB_THRESHOLD_SECS)) { + logger.log(Level.INFO, "Dropping test database {0}", db.getId()); + if (db.isDropProtectionEnabled()) { + Database updatedDatabase = + databaseAdminClient.newDatabaseBuilder(db.getId()).disableDropProtection().build(); + databaseAdminClient + .updateDatabase(updatedDatabase, DatabaseField.DROP_PROTECTION) + .get(); } - } catch (SpannerException e) { - logger.log(Level.SEVERE, "Failed to drop test database " + db.getId(), e); + db.drop(); + ++numDropped; } + } catch (SpannerException | ExecutionException | InterruptedException e) { + logger.log(Level.SEVERE, "Failed to drop test database " + db.getId(), e); } - page = page.getNextPage(); } logger.log(Level.INFO, "Dropped {0} test database(s)", numDropped); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestWithClosedSessionsEnv.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestWithClosedSessionsEnv.java index 0fa5ff27d82..62a25f9dc4d 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestWithClosedSessionsEnv.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestWithClosedSessionsEnv.java @@ -46,7 +46,7 @@ private static class SpannerWithClosedSessionsImpl extends SpannerImpl { @Override DatabaseClientImpl createDatabaseClient(String clientId, SessionPool pool) { - return new DatabaseClientWithClosedSessionImpl(clientId, pool); + return new DatabaseClientWithClosedSessionImpl(clientId, pool, tracer); } } @@ -58,8 +58,8 @@ public static class DatabaseClientWithClosedSessionImpl extends DatabaseClientIm private boolean invalidateNextSession = false; private boolean allowReplacing = true; - DatabaseClientWithClosedSessionImpl(String clientId, SessionPool pool) { - super(clientId, pool); + DatabaseClientWithClosedSessionImpl(String clientId, SessionPool pool, TraceWrapper tracer) { + super(clientId, pool, tracer); } /** Invalidate the next session that is checked out from the pool. */ diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetrySpanTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetrySpanTest.java new file mode 100644 index 00000000000..956607e0231 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetrySpanTest.java @@ -0,0 +1,563 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import com.google.api.gax.grpc.testing.LocalChannelProvider; +import com.google.cloud.NoCredentials; +import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; +import com.google.common.collect.ImmutableList; +import com.google.protobuf.ListValue; +import com.google.spanner.v1.ResultSetMetadata; +import com.google.spanner.v1.StructType; +import com.google.spanner.v1.StructType.Field; +import com.google.spanner.v1.TypeCode; +import io.grpc.Server; +import io.grpc.Status; +import io.grpc.inprocess.InProcessServerBuilder; +import io.opencensus.trace.Tracing; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.data.EventData; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assume; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@Category(TracerTest.class) +@RunWith(JUnit4.class) +public class OpenTelemetrySpanTest { + + private static final String TEST_PROJECT = "my-project"; + private static final String TEST_INSTANCE = "my-instance"; + private static final String TEST_DATABASE = "my-database"; + private static LocalChannelProvider channelProvider; + private static MockSpannerServiceImpl mockSpanner; + private Spanner spanner; + private DatabaseClient client; + private static Server server; + private static InMemorySpanExporter spanExporter; + + private static FailOnOverkillTraceComponentImpl failOnOverkillTraceComponent = + new FailOnOverkillTraceComponentImpl(); + + private static final Statement SELECT1 = Statement.of("SELECT 1 AS COL1"); + + private static final ResultSetMetadata SELECT1_METADATA = + ResultSetMetadata.newBuilder() + .setRowType( + StructType.newBuilder() + .addFields( + Field.newBuilder() + .setName("COL1") + .setType( + com.google.spanner.v1.Type.newBuilder() + .setCode(TypeCode.INT64) + .build()) + .build()) + .build()) + .build(); + private static final com.google.spanner.v1.ResultSet SELECT1_RESULTSET = + com.google.spanner.v1.ResultSet.newBuilder() + .addRows( + ListValue.newBuilder() + .addValues(com.google.protobuf.Value.newBuilder().setStringValue("1").build()) + .build()) + .setMetadata(SELECT1_METADATA) + .build(); + private static final Statement UPDATE_STATEMENT = + Statement.of("UPDATE FOO SET BAR=1 WHERE BAZ=2"); + private static final long UPDATE_COUNT = 1L; + private static final Statement INVALID_UPDATE_STATEMENT = + Statement.of("UPDATE NON_EXISTENT_TABLE SET BAR=1 WHERE BAZ=2"); + + private List expectedBatchCreateSessionsRequestEvents = + ImmutableList.of("Requesting 25 sessions", "Request for 25 sessions returned 25 sessions"); + + private int expectedBatchCreateSessionsRequestEventsCount = 2; + + private List expectedBatchCreateSessionsEvents = ImmutableList.of("Creating 25 sessions"); + + private int expectedBatchCreateSessionsEventsCount = 1; + + private List expectedExecuteStreamingQueryEvents = + ImmutableList.of("Starting/Resuming stream"); + + private int expectedExecuteStreamingQueryEventsCount = 1; + + private List expectedReadOnlyTransactionSingleUseEvents = + ImmutableList.of( + "Acquiring session", + "No session available", + "Creating sessions", + "Waiting for a session to come available", + "Using Session"); + + private int expectedReadOnlyTransactionSingleUseEventsCount = 5; + + private List expectedReadOnlyTransactionMultiUseEvents = + ImmutableList.of( + "Acquiring session", + "No session available", + "Creating sessions", + "Waiting for a session to come available", + "Using Session", + "Creating Transaction", + "Transaction Creation Done"); + + private int expectedReadOnlyTransactionMultiUseEventsCount = 7; + + private List expectedReadWriteTransactionErrorEvents = + ImmutableList.of( + "Acquiring session", + "No session available", + "Creating sessions", + "Waiting for a session to come available", + "Using Session", + "Starting Transaction Attempt", + "Transaction Attempt Failed in user operation", + "exception"); + + private int expectedReadWriteTransactionErrorEventsCount = 8; + private List expectedReadWriteTransactionEvents = + ImmutableList.of( + "Acquiring session", + "No session available", + "Creating sessions", + "Waiting for a session to come available", + "Using Session", + "Starting Transaction Attempt", + "Starting Commit", + "Commit Done", + "Transaction Attempt Succeeded"); + + private int expectedReadWriteTransactionCount = 9; + private List expectedReadWriteTransactionErrorWithBeginTransactionEvents = + ImmutableList.of( + "Acquiring session", + "No session available", + "Creating sessions", + "Waiting for a session to come available", + "Using Session", + "Starting Transaction Attempt", + "Transaction Attempt Aborted in user operation. Retrying", + "Creating Transaction", + "Transaction Creation Done", + "Starting Commit", + "Commit Done", + "Transaction Attempt Succeeded"); + + private int expectedReadWriteTransactionErrorWithBeginTransactionEventsCount = 13; + private List expectedReadOnlyTransactionSpans = + ImmutableList.of( + "CloudSpannerOperation.BatchCreateSessionsRequest", + "CloudSpannerOperation.ExecuteStreamingQuery", + "CloudSpannerOperation.BatchCreateSessions", + "CloudSpanner.ReadOnlyTransaction", + "SessionPool.WaitForSession"); + + private List expectedReadWriteTransactionWithCommitSpans = + ImmutableList.of( + "CloudSpannerOperation.BatchCreateSessionsRequest", + "CloudSpannerOperation.Commit", + "CloudSpannerOperation.BatchCreateSessions", + "CloudSpanner.ReadWriteTransaction", + "SessionPool.WaitForSession"); + + private List expectedReadWriteTransactionSpans = + ImmutableList.of( + "CloudSpannerOperation.BatchCreateSessionsRequest", + "CloudSpannerOperation.BatchCreateSessions", + "CloudSpanner.ReadWriteTransaction", + "SessionPool.WaitForSession"); + + private List expectedReadWriteTransactionWithCommitAndBeginTransactionSpans = + ImmutableList.of( + "CloudSpannerOperation.BeginTransaction", + "CloudSpannerOperation.BatchCreateSessionsRequest", + "CloudSpannerOperation.Commit", + "CloudSpannerOperation.BatchCreateSessions", + "CloudSpanner.ReadWriteTransaction", + "SessionPool.WaitForSession"); + + @BeforeClass + public static void setupOpenTelemetry() { + SpannerOptions.resetActiveTracingFramework(); + SpannerOptions.enableOpenTelemetryTraces(); + } + + @BeforeClass + public static void startStaticServer() throws Exception { + // Incorporating OpenCensus tracer to ensure that OpenTraces traces are utilized if enabled, + // regardless of the presence of OpenCensus tracer. + java.lang.reflect.Field field = Tracing.class.getDeclaredField("traceComponent"); + field.setAccessible(true); + java.lang.reflect.Field modifiersField = null; + try { + modifiersField = java.lang.reflect.Field.class.getDeclaredField("modifiers"); + } catch (NoSuchFieldException e) { + // Halt the test and ignore it. + Assume.assumeTrue( + "Skipping test as reflection is not allowed on reflection class in this JDK build", + false); + } + modifiersField.setAccessible(true); + // Remove the final modifier from the 'traceComponent' field. + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + field.set(null, failOnOverkillTraceComponent); + + mockSpanner = new MockSpannerServiceImpl(); + mockSpanner.setAbortProbability(0.0D); // We don't want any unpredictable aborted transactions. + mockSpanner.putStatementResult(StatementResult.query(SELECT1, SELECT1_RESULTSET)); + mockSpanner.putStatementResult(StatementResult.update(UPDATE_STATEMENT, UPDATE_COUNT)); + mockSpanner.putStatementResult( + StatementResult.exception( + INVALID_UPDATE_STATEMENT, + Status.INVALID_ARGUMENT.withDescription("invalid statement").asRuntimeException())); + String uniqueName = InProcessServerBuilder.generateName(); + server = InProcessServerBuilder.forName(uniqueName).addService(mockSpanner).build().start(); + + channelProvider = LocalChannelProvider.create(uniqueName); + failOnOverkillTraceComponent.clearSpans(); + failOnOverkillTraceComponent.clearAnnotations(); + } + + @AfterClass + public static void stopServer() throws InterruptedException { + if (server != null) { + server.shutdown(); + server.awaitTermination(); + } + } + + @Before + public void setUp() throws Exception { + spanExporter = InMemorySpanExporter.create(); + + SdkTracerProvider tracerProvider = + SdkTracerProvider.builder() + .addSpanProcessor(SimpleSpanProcessor.create(spanExporter)) + .build(); + + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder() + .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())) + .setTracerProvider(tracerProvider) + .build(); + + SpannerOptions.Builder builder = + SpannerOptions.newBuilder() + .setProjectId(TEST_PROJECT) + .setChannelProvider(channelProvider) + .setOpenTelemetry(openTelemetry) + .setCredentials(NoCredentials.getInstance()) + .setSessionPoolOption(SessionPoolOptions.newBuilder().setMinSessions(0).build()); + + spanner = builder.build().getService(); + + client = spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + } + + @After + public void tearDown() { + spanner.close(); + mockSpanner.reset(); + mockSpanner.removeAllExecutionTimes(); + spanExporter.reset(); + } + + @Test + public void singleUse() { + try (ResultSet rs = client.singleUse().executeQuery(SELECT1)) { + while (rs.next()) { + // Just consume the result set. + } + } + + // OpenCensus spans should be 0 as OpenTelemetry is enabled. + assertEquals(failOnOverkillTraceComponent.getSpans().size(), 0); + + List actualSpanItems = new ArrayList<>(); + spanExporter + .getFinishedSpanItems() + .forEach( + spanItem -> { + actualSpanItems.add(spanItem.getName()); + switch (spanItem.getName()) { + case "CloudSpannerOperation.BatchCreateSessionsRequest": + verifyRequestEvents( + spanItem, + expectedBatchCreateSessionsRequestEvents, + expectedBatchCreateSessionsRequestEventsCount); + break; + case "CloudSpannerOperation.BatchCreateSessions": + verifyRequestEvents( + spanItem, + expectedBatchCreateSessionsEvents, + expectedBatchCreateSessionsEventsCount); + break; + case "CloudSpannerOperation.ExecuteStreamingQuery": + verifyRequestEvents( + spanItem, + expectedExecuteStreamingQueryEvents, + expectedExecuteStreamingQueryEventsCount); + break; + case "CloudSpanner.ReadOnlyTransaction": + verifyRequestEvents( + spanItem, + expectedReadOnlyTransactionSingleUseEvents, + expectedReadOnlyTransactionSingleUseEventsCount); + break; + case "SessionPool.WaitForSession": + assertEquals(0, spanItem.getEvents().size()); + break; + default: + assert false; + } + }); + + verifySpans(actualSpanItems, expectedReadOnlyTransactionSpans); + } + + @Test + public void multiUse() { + try (ReadOnlyTransaction tx = client.readOnlyTransaction()) { + try (ResultSet rs = tx.executeQuery(SELECT1)) { + while (rs.next()) { + // Just consume the result set. + } + } + } + + List actualSpanItems = new ArrayList<>(); + spanExporter + .getFinishedSpanItems() + .forEach( + spanItem -> { + actualSpanItems.add(spanItem.getName()); + switch (spanItem.getName()) { + case "CloudSpannerOperation.BatchCreateSessionsRequest": + verifyRequestEvents( + spanItem, + expectedBatchCreateSessionsRequestEvents, + expectedBatchCreateSessionsRequestEventsCount); + break; + case "CloudSpannerOperation.BatchCreateSessions": + verifyRequestEvents( + spanItem, + expectedBatchCreateSessionsEvents, + expectedBatchCreateSessionsEventsCount); + break; + case "CloudSpannerOperation.ExecuteStreamingQuery": + verifyRequestEvents( + spanItem, + expectedExecuteStreamingQueryEvents, + expectedExecuteStreamingQueryEventsCount); + break; + case "SessionPool.WaitForSession": + assertEquals(0, spanItem.getEvents().size()); + break; + case "CloudSpanner.ReadOnlyTransaction": + verifyRequestEvents( + spanItem, + expectedReadOnlyTransactionMultiUseEvents, + expectedReadOnlyTransactionMultiUseEventsCount); + break; + default: + assert false; + } + }); + + verifySpans(actualSpanItems, expectedReadOnlyTransactionSpans); + } + + @Test + public void transactionRunner() { + TransactionRunner runner = client.readWriteTransaction(); + runner.run(transaction -> transaction.executeUpdate(UPDATE_STATEMENT)); + + List actualSpanItems = new ArrayList<>(); + spanExporter + .getFinishedSpanItems() + .forEach( + spanItem -> { + actualSpanItems.add(spanItem.getName()); + switch (spanItem.getName()) { + case "CloudSpannerOperation.BatchCreateSessionsRequest": + verifyRequestEvents( + spanItem, + expectedBatchCreateSessionsRequestEvents, + expectedBatchCreateSessionsRequestEventsCount); + break; + case "CloudSpannerOperation.BatchCreateSessions": + verifyRequestEvents( + spanItem, + expectedBatchCreateSessionsEvents, + expectedBatchCreateSessionsEventsCount); + break; + case "SessionPool.WaitForSession": + case "CloudSpannerOperation.Commit": + assertEquals(0, spanItem.getEvents().size()); + break; + case "CloudSpanner.ReadWriteTransaction": + verifyRequestEvents( + spanItem, + expectedReadWriteTransactionEvents, + expectedReadWriteTransactionCount); + break; + default: + assert false; + } + }); + + verifySpans(actualSpanItems, expectedReadWriteTransactionWithCommitSpans); + } + + @Test + public void transactionRunnerWithError() { + TransactionRunner runner = client.readWriteTransaction(); + SpannerException e = + assertThrows( + SpannerException.class, + () -> runner.run(transaction -> transaction.executeUpdate(INVALID_UPDATE_STATEMENT))); + assertEquals(ErrorCode.INVALID_ARGUMENT, e.getErrorCode()); + + List actualSpanItems = new ArrayList<>(); + spanExporter + .getFinishedSpanItems() + .forEach( + spanItem -> { + actualSpanItems.add(spanItem.getName()); + switch (spanItem.getName()) { + case "CloudSpannerOperation.BatchCreateSessionsRequest": + verifyRequestEvents( + spanItem, + expectedBatchCreateSessionsRequestEvents, + expectedBatchCreateSessionsRequestEventsCount); + break; + case "CloudSpannerOperation.BatchCreateSessions": + verifyRequestEvents( + spanItem, + expectedBatchCreateSessionsEvents, + expectedBatchCreateSessionsEventsCount); + break; + case "SessionPool.WaitForSession": + assertEquals(0, spanItem.getEvents().size()); + break; + case "CloudSpanner.ReadWriteTransaction": + verifyRequestEvents( + spanItem, + expectedReadWriteTransactionErrorEvents, + expectedReadWriteTransactionErrorEventsCount); + break; + default: + assert false; + } + }); + + verifySpans(actualSpanItems, expectedReadWriteTransactionSpans); + } + + @Test + public void transactionRunnerWithFailedAndBeginTransaction() { + Long updateCount = + client + .readWriteTransaction() + .run( + transaction -> { + // This update statement carries the BeginTransaction, but fails. This will + // cause the entire transaction to be retried with an explicit + // BeginTransaction RPC to ensure all statements in the transaction are + // actually executed against the same transaction. + SpannerException e = + assertThrows( + SpannerException.class, + () -> transaction.executeUpdate(INVALID_UPDATE_STATEMENT)); + assertEquals(ErrorCode.INVALID_ARGUMENT, e.getErrorCode()); + return transaction.executeUpdate(UPDATE_STATEMENT); + }); + + List actualSpanItems = new ArrayList<>(); + spanExporter + .getFinishedSpanItems() + .forEach( + spanItem -> { + actualSpanItems.add(spanItem.getName()); + switch (spanItem.getName()) { + case "CloudSpannerOperation.BatchCreateSessionsRequest": + verifyRequestEvents( + spanItem, + expectedBatchCreateSessionsRequestEvents, + expectedBatchCreateSessionsRequestEventsCount); + break; + case "CloudSpannerOperation.BatchCreateSessions": + verifyRequestEvents( + spanItem, + expectedBatchCreateSessionsEvents, + expectedBatchCreateSessionsEventsCount); + break; + case "SessionPool.WaitForSession": + case "CloudSpannerOperation.Commit": + case "CloudSpannerOperation.BeginTransaction": + assertEquals(0, spanItem.getEvents().size()); + break; + case "CloudSpanner.ReadWriteTransaction": + verifyRequestEvents( + spanItem, + expectedReadWriteTransactionErrorWithBeginTransactionEvents, + expectedReadWriteTransactionErrorWithBeginTransactionEventsCount); + break; + default: + assert false; + } + }); + + verifySpans(actualSpanItems, expectedReadWriteTransactionWithCommitAndBeginTransactionSpans); + } + + private void verifyRequestEvents(SpanData spanItem, List expectedEvents, int eventCount) { + List eventNames = + spanItem.getEvents().stream().map(EventData::getName).collect(Collectors.toList()); + assertEquals(eventCount, spanItem.getEvents().size()); + assertEquals( + eventNames.stream().distinct().sorted().collect(Collectors.toList()), + expectedEvents.stream().sorted().collect(Collectors.toList())); + } + + private static void verifySpans(List actualSpanItems, List expectedSpansItems) { + assertEquals( + actualSpanItems.stream().distinct().sorted().collect(Collectors.toList()), + expectedSpansItems.stream().sorted().collect(Collectors.toList())); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResumableStreamIteratorTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResumableStreamIteratorTest.java index 06c1725d76a..217e818d42c 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResumableStreamIteratorTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResumableStreamIteratorTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import com.google.api.client.util.BackOff; import com.google.cloud.spanner.AbstractResultSet.ResumableStreamIterator; @@ -36,8 +37,10 @@ import io.grpc.Status; import io.grpc.StatusRuntimeException; import io.grpc.protobuf.ProtoUtils; -import io.opencensus.trace.EndSpanOptions; import io.opencensus.trace.Span; +import io.opencensus.trace.Tracing; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.context.Scope; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; @@ -132,15 +135,19 @@ public boolean isWithBeginTransaction() { @Before public void setUp() { + SpannerOptions.resetActiveTracingFramework(); + SpannerOptions.enableOpenTelemetryTraces(); initWithLimit(Integer.MAX_VALUE); } private void initWithLimit(int maxBufferSize) { + resumableStreamIterator = new AbstractResultSet.ResumableStreamIterator( maxBufferSize, "", - null, + new OpenTelemetrySpan(mock(io.opentelemetry.api.trace.Span.class)), + new TraceWrapper(Tracing.getTracer(), OpenTelemetry.noop().getTracer("")), SpannerStubSettings.newBuilder().executeStreamingSqlSettings().getRetrySettings(), SpannerStubSettings.newBuilder().executeStreamingSqlSettings().getRetryableCodes()) { @Override @@ -163,12 +170,39 @@ public void simple() { } @Test - public void closedSpan() { + public void closedOTSpan() { + SpannerOptions.resetActiveTracingFramework(); + SpannerOptions.enableOpenTelemetryTraces(); Assume.assumeTrue( "This test is only supported on JDK11 and lower", JavaVersionUtil.getJavaMajorVersion() < 12); - Span span = mock(Span.class); + io.opentelemetry.api.trace.Span oTspan = mock(io.opentelemetry.api.trace.Span.class); + ISpan span = new OpenTelemetrySpan(oTspan); + when(oTspan.makeCurrent()).thenReturn(mock(Scope.class)); + setInternalState(ResumableStreamIterator.class, this.resumableStreamIterator, "span", span); + + ResultSetStream s1 = Mockito.mock(ResultSetStream.class); + Mockito.when(starter.startStream(null)).thenReturn(new ResultSetIterator(s1)); + Mockito.when(s1.next()) + .thenReturn(resultSet(ByteString.copyFromUtf8("r1"), "a")) + .thenReturn(resultSet(ByteString.copyFromUtf8("r2"), "b")) + .thenReturn(null); + assertThat(consume(resumableStreamIterator)).containsExactly("a", "b").inOrder(); + + resumableStreamIterator.close("closed"); + verify(oTspan).end(); + } + + @Test + public void closedOCSpan() { + SpannerOptions.resetActiveTracingFramework(); + SpannerOptions.enableOpenCensusTraces(); + Assume.assumeTrue( + "This test is only supported on JDK11 and lower", + JavaVersionUtil.getJavaMajorVersion() < 12); + Span mockSpan = mock(Span.class); + ISpan span = new OpenCensusSpan(mockSpan); setInternalState(ResumableStreamIterator.class, this.resumableStreamIterator, "span", span); ResultSetStream s1 = Mockito.mock(ResultSetStream.class); @@ -180,7 +214,7 @@ public void closedSpan() { assertThat(consume(resumableStreamIterator)).containsExactly("a", "b").inOrder(); resumableStreamIterator.close("closed"); - verify(span).end(EndSpanOptions.builder().setSampleToLocalSpanStore(true).build()); + verify(mockSpan).end(OpenCensusSpan.END_SPAN_OPTIONS); } @Test diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionClientTests.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionClientTests.java index 8af1323238a..c8d9bc39339 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionClientTests.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionClientTests.java @@ -17,6 +17,8 @@ package com.google.cloud.spanner; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -27,6 +29,8 @@ import com.google.cloud.spanner.SessionClient.SessionConsumer; import com.google.cloud.spanner.spi.v1.SpannerRpc; import com.google.cloud.spanner.spi.v1.SpannerRpc.Option; +import io.opencensus.trace.Tracing; +import io.opentelemetry.api.OpenTelemetry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -83,6 +87,9 @@ public static Collection data() { @Mock private SpannerImpl spanner; @Mock private SpannerRpc rpc; @Mock private SpannerOptions spannerOptions; + private final TraceWrapper tracer = + new TraceWrapper(Tracing.getTracer(), OpenTelemetry.noop().getTracer("")); + @Mock private ISpan span; @Captor ArgumentCaptor> options; @Before @@ -109,6 +116,10 @@ public ScheduledExecutorService get() { when(spannerOptions.getRetrySettings()).thenReturn(RetrySettings.newBuilder().build()); when(spannerOptions.getClock()).thenReturn(NanoClock.getDefaultClock()); when(spanner.getOptions()).thenReturn(spannerOptions); + when(spanner.getTracer()).thenReturn(tracer); + doNothing().when(span).setStatus(any(Throwable.class)); + doNothing().when(span).end(); + doNothing().when(span).addAnnotation("Starting Commit"); when(spanner.getRpc()).thenReturn(rpc); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionImplTest.java index a920fdb67dc..87edb64134c 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionImplTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -48,7 +49,9 @@ import com.google.spanner.v1.RollbackRequest; import com.google.spanner.v1.Session; import com.google.spanner.v1.Transaction; -import io.opencensus.trace.Span; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Scope; import java.text.ParseException; import java.util.Calendar; import java.util.Collections; @@ -58,6 +61,7 @@ import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -76,6 +80,12 @@ public class SessionImplTest { @Captor private ArgumentCaptor> optionsCaptor; private Map options; + @BeforeClass + public static void setupOpenTelemetry() { + SpannerOptions.resetActiveTracingFramework(); + SpannerOptions.enableOpenTelemetryTraces(); + } + @SuppressWarnings("unchecked") @Before public void setUp() { @@ -90,6 +100,7 @@ public void setUp() { when(transportOptions.getExecutorFactory()).thenReturn(mock(ExecutorFactory.class)); when(spannerOptions.getTransportOptions()).thenReturn(transportOptions); when(spannerOptions.getSessionPoolOptions()).thenReturn(mock(SessionPoolOptions.class)); + when(spannerOptions.getOpenTelemetry()).thenReturn(OpenTelemetry.noop()); @SuppressWarnings("resource") SpannerImpl spanner = new SpannerImpl(rpc, spannerOptions); String dbName = "projects/p1/instances/i1/databases/d1"; @@ -127,7 +138,10 @@ public void setUp() { .thenReturn( SpannerStubSettings.newBuilder().executeStreamingSqlSettings().getRetryableCodes()); session = spanner.getSessionClient(db).createSession(); - ((SessionImpl) session).setCurrentSpan(mock(Span.class)); + Span oTspan = mock(Span.class); + ISpan span = new OpenTelemetrySpan(oTspan); + when(oTspan.makeCurrent()).thenReturn(mock(Scope.class)); + ((SessionImpl) session).setCurrentSpan(span); // We expect the same options, "options", on all calls on "session". options = optionsCaptor.getValue(); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolMaintainerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolMaintainerTest.java index 010e13c4390..127ffd3bff6 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolMaintainerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolMaintainerTest.java @@ -28,6 +28,8 @@ import com.google.cloud.spanner.SessionPool.PooledSessionFuture; import com.google.cloud.spanner.SessionPool.Position; import com.google.cloud.spanner.SessionPool.SessionConsumerImpl; +import io.opencensus.trace.Tracing; +import io.opentelemetry.api.OpenTelemetry; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -117,7 +119,13 @@ private SessionPool createPool() throws Exception { // otherwise impossible to know which session exactly is getting pinged at what point in time. SessionPool pool = SessionPool.createPool( - options, new TestExecutorFactory(), client.getSessionClient(db), clock, Position.FIRST); + options, + new TestExecutorFactory(), + client.getSessionClient(db), + clock, + Position.FIRST, + new TraceWrapper(Tracing.getTracer(), OpenTelemetry.noop().getTracer("")), + OpenTelemetry.noop()); pool.idleSessionRemovedListener = input -> { idledSessions.add(input); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolStressTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolStressTest.java index 535423bb9e4..bb263f04e5f 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolStressTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolStressTest.java @@ -34,6 +34,8 @@ import com.google.common.util.concurrent.Uninterruptibles; import com.google.protobuf.ByteString; import com.google.protobuf.Empty; +import io.opencensus.trace.Tracing; +import io.opentelemetry.api.OpenTelemetry; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -248,7 +250,9 @@ public void stressTest() throws Exception { new TestExecutorFactory(), mockSpanner.getSessionClient(db), clock, - Position.RANDOM); + Position.RANDOM, + new TraceWrapper(Tracing.getTracer(), OpenTelemetry.noop().getTracer("")), + OpenTelemetry.noop()); pool.idleSessionRemovedListener = pooled -> { String name = pooled.getName(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java index dc06c1c5b3e..e1c295c14de 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java @@ -16,9 +16,18 @@ package com.google.cloud.spanner; +import static com.google.cloud.spanner.MetricRegistryConstants.GET_SESSION_TIMEOUTS; +import static com.google.cloud.spanner.MetricRegistryConstants.MAX_ALLOWED_SESSIONS; +import static com.google.cloud.spanner.MetricRegistryConstants.MAX_IN_USE_SESSIONS; +import static com.google.cloud.spanner.MetricRegistryConstants.METRIC_PREFIX; +import static com.google.cloud.spanner.MetricRegistryConstants.NUM_ACQUIRED_SESSIONS; import static com.google.cloud.spanner.MetricRegistryConstants.NUM_IN_USE_SESSIONS; import static com.google.cloud.spanner.MetricRegistryConstants.NUM_READ_SESSIONS; +import static com.google.cloud.spanner.MetricRegistryConstants.NUM_RELEASED_SESSIONS; +import static com.google.cloud.spanner.MetricRegistryConstants.NUM_SESSIONS_AVAILABLE; import static com.google.cloud.spanner.MetricRegistryConstants.NUM_SESSIONS_BEING_PREPARED; +import static com.google.cloud.spanner.MetricRegistryConstants.NUM_SESSIONS_IN_POOL; +import static com.google.cloud.spanner.MetricRegistryConstants.NUM_SESSIONS_IN_USE; import static com.google.cloud.spanner.MetricRegistryConstants.NUM_WRITE_SESSIONS; import static com.google.cloud.spanner.MetricRegistryConstants.SPANNER_DEFAULT_LABEL_VALUES; import static com.google.cloud.spanner.MetricRegistryConstants.SPANNER_LABEL_KEYS; @@ -31,8 +40,10 @@ import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doThrow; @@ -74,7 +85,17 @@ import com.google.spanner.v1.RollbackRequest; import io.opencensus.metrics.LabelValue; import io.opencensus.metrics.MetricRegistry; -import io.opencensus.trace.Span; +import io.opencensus.metrics.Metrics; +import io.opencensus.trace.Tracing; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Scope; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; @@ -125,18 +146,32 @@ public class SessionPoolTest extends BaseSessionPoolTest { private String sessionName = String.format("%s/sessions/s", db.getName()); private String TEST_DATABASE_ROLE = "my-role"; + private final TraceWrapper tracer = + new TraceWrapper(Tracing.getTracer(), OpenTelemetry.noop().getTracer("")); + @Parameters(name = "min sessions = {0}") public static Collection data() { return Arrays.asList(new Object[][] {{0}, {1}}); } private SessionPool createPool() { - return SessionPool.createPool(options, new TestExecutorFactory(), client.getSessionClient(db)); + return SessionPool.createPool( + options, + new TestExecutorFactory(), + client.getSessionClient(db), + tracer, + OpenTelemetry.noop()); } private SessionPool createPool(Clock clock) { return SessionPool.createPool( - options, new TestExecutorFactory(), client.getSessionClient(db), clock, Position.RANDOM); + options, + new TestExecutorFactory(), + client.getSessionClient(db), + clock, + Position.RANDOM, + tracer, + OpenTelemetry.noop()); } private SessionPool createPool( @@ -149,7 +184,30 @@ private SessionPool createPool( clock, Position.RANDOM, metricRegistry, - labelValues); + tracer, + labelValues, + OpenTelemetry.noop(), + null); + } + + private SessionPool createPool( + Clock clock, + MetricRegistry metricRegistry, + List labelValues, + OpenTelemetry openTelemetry, + Attributes attributes) { + return SessionPool.createPool( + options, + TEST_DATABASE_ROLE, + new TestExecutorFactory(), + client.getSessionClient(db), + clock, + Position.RANDOM, + metricRegistry, + tracer, + labelValues, + openTelemetry, + attributes); } @BeforeClass @@ -168,6 +226,8 @@ public static void resetLogging() { @Before public void setUp() { initMocks(this); + SpannerOptions.resetActiveTracingFramework(); + SpannerOptions.enableOpenTelemetryTraces(); when(client.getOptions()).thenReturn(spannerOptions); when(client.getSessionClient(db)).thenReturn(sessionClient); when(sessionClient.getSpanner()).thenReturn(client); @@ -1166,8 +1226,8 @@ public void blockAndTimeoutOnPoolExhaustion() throws Exception { latch.await(); // Wait until the request has timed out. int waitCount = 0; - while (pool.getNumWaiterTimeouts() == 0L && waitCount < 1000) { - Thread.sleep(5L); + while (pool.getNumWaiterTimeouts() == 0L && waitCount < 5000) { + Thread.sleep(1L); waitCount++; } // Return the checked out session to the pool so the async request will get a session and @@ -1214,8 +1274,8 @@ public void blockAndTimeoutOnPoolExhaustion_withAcquireSessionTimeout() throws E latch.await(); // Wait until the request has timed out. int waitCount = 0; - while (pool.getNumWaiterTimeouts() == 0L && waitCount < 1000) { - Thread.sleep(5L); + while (pool.getNumWaiterTimeouts() == 0L && waitCount < 5000) { + Thread.sleep(1L); waitCount++; } // Return the checked out session to the pool so the async request will get a session and @@ -1375,19 +1435,27 @@ public void testSessionNotFoundReadWriteTransaction() { final SessionImpl closedSession = mock(SessionImpl.class); when(closedSession.getName()) .thenReturn("projects/dummy/instances/dummy/database/dummy/sessions/session-closed"); + + Span oTspan = mock(Span.class); + ISpan span = new OpenTelemetrySpan(oTspan); + when(oTspan.makeCurrent()).thenReturn(mock(Scope.class)); + final TransactionContextImpl closedTransactionContext = TransactionContextImpl.newBuilder() .setSession(closedSession) .setOptions(Options.fromTransactionOptions()) .setRpc(rpc) + .setTracer(tracer) + .setSpan(span) .build(); when(closedSession.asyncClose()) .thenReturn(ApiFutures.immediateFuture(Empty.getDefaultInstance())); when(closedSession.newTransaction(Options.fromTransactionOptions())) .thenReturn(closedTransactionContext); when(closedSession.beginTransactionAsync(any(), eq(true))).thenThrow(sessionNotFound); + when(closedSession.getTracer()).thenReturn(tracer); TransactionRunnerImpl closedTransactionRunner = new TransactionRunnerImpl(closedSession); - closedTransactionRunner.setSpan(mock(Span.class)); + closedTransactionRunner.setSpan(span); when(closedSession.readWriteTransaction()).thenReturn(closedTransactionRunner); final SessionImpl openSession = mock(SessionImpl.class); @@ -1400,8 +1468,9 @@ public void testSessionNotFoundReadWriteTransaction() { .thenReturn(openTransactionContext); when(openSession.beginTransactionAsync(any(), eq(true))) .thenReturn(ApiFutures.immediateFuture(ByteString.copyFromUtf8("open-txn"))); + when(openSession.getTracer()).thenReturn(tracer); TransactionRunnerImpl openTransactionRunner = new TransactionRunnerImpl(openSession); - openTransactionRunner.setSpan(mock(Span.class)); + openTransactionRunner.setSpan(span); when(openSession.readWriteTransaction()).thenReturn(openTransactionRunner); ResultSet openResultSet = mock(ResultSet.class); @@ -1455,7 +1524,12 @@ public void testSessionNotFoundReadWriteTransaction() { when(spannerOptions.getDatabaseRole()).thenReturn("role"); when(spanner.getOptions()).thenReturn(spannerOptions); SessionPool pool = - SessionPool.createPool(options, new TestExecutorFactory(), spanner.getSessionClient(db)); + SessionPool.createPool( + options, + new TestExecutorFactory(), + spanner.getSessionClient(db), + tracer, + OpenTelemetry.noop()); try (PooledSessionFuture readWriteSession = pool.getSession()) { TransactionRunner runner = readWriteSession.readWriteTransaction(); try { @@ -1554,7 +1628,7 @@ public void testSessionNotFoundWrite() { FakeClock clock = new FakeClock(); clock.currentTimeMillis.set(System.currentTimeMillis()); pool = createPool(clock); - DatabaseClientImpl impl = new DatabaseClientImpl(pool); + DatabaseClientImpl impl = new DatabaseClientImpl(pool, tracer); assertThat(impl.write(mutations)).isNotNull(); } @@ -1596,7 +1670,7 @@ public void testSessionNotFoundWriteAtLeastOnce() { FakeClock clock = new FakeClock(); clock.currentTimeMillis.set(System.currentTimeMillis()); pool = createPool(clock); - DatabaseClientImpl impl = new DatabaseClientImpl(pool); + DatabaseClientImpl impl = new DatabaseClientImpl(pool, tracer); assertThat(impl.writeAtLeastOnce(mutations)).isNotNull(); } @@ -1635,19 +1709,20 @@ public void testSessionNotFoundPartitionedUpdate() { FakeClock clock = new FakeClock(); clock.currentTimeMillis.set(System.currentTimeMillis()); pool = createPool(clock); - DatabaseClientImpl impl = new DatabaseClientImpl(pool); + DatabaseClientImpl impl = new DatabaseClientImpl(pool, mock(TraceWrapper.class)); assertThat(impl.executePartitionedUpdate(statement)).isEqualTo(1L); } + @SuppressWarnings("rawtypes") @Test - public void testSessionMetrics() throws Exception { + public void testOpenCensusSessionMetrics() throws Exception { // Create a session pool with max 2 session and a low timeout for waiting for a session. options = SessionPoolOptions.newBuilder() .setMinSessions(1) .setMaxSessions(2) - .setMaxIdleSessions(0) .setInitialWaitForSessionTimeoutMillis(50L) + .setAcquireSessionTimeout(null) .build(); FakeClock clock = new FakeClock(); clock.currentTimeMillis.set(System.currentTimeMillis()); @@ -1670,42 +1745,42 @@ public void testSessionMetrics() throws Exception { assertThat(record.getMetrics().size()).isEqualTo(6); List maxInUseSessions = - record.getMetrics().get(MetricRegistryConstants.MAX_IN_USE_SESSIONS); + record.getMetrics().get(METRIC_PREFIX + MAX_IN_USE_SESSIONS); assertThat(maxInUseSessions.size()).isEqualTo(1); assertThat(maxInUseSessions.get(0).value()).isEqualTo(2L); assertThat(maxInUseSessions.get(0).keys()).isEqualTo(SPANNER_LABEL_KEYS); assertThat(maxInUseSessions.get(0).values()).isEqualTo(labelValues); List getSessionsTimeouts = - record.getMetrics().get(MetricRegistryConstants.GET_SESSION_TIMEOUTS); + record.getMetrics().get(METRIC_PREFIX + GET_SESSION_TIMEOUTS); assertThat(getSessionsTimeouts.size()).isEqualTo(1); assertThat(getSessionsTimeouts.get(0).value()).isAtMost(1L); assertThat(getSessionsTimeouts.get(0).keys()).isEqualTo(SPANNER_LABEL_KEYS); assertThat(getSessionsTimeouts.get(0).values()).isEqualTo(labelValues); List numAcquiredSessions = - record.getMetrics().get(MetricRegistryConstants.NUM_ACQUIRED_SESSIONS); + record.getMetrics().get(METRIC_PREFIX + NUM_ACQUIRED_SESSIONS); assertThat(numAcquiredSessions.size()).isEqualTo(1); assertThat(numAcquiredSessions.get(0).value()).isEqualTo(2L); assertThat(numAcquiredSessions.get(0).keys()).isEqualTo(SPANNER_LABEL_KEYS); assertThat(numAcquiredSessions.get(0).values()).isEqualTo(labelValues); List numReleasedSessions = - record.getMetrics().get(MetricRegistryConstants.NUM_RELEASED_SESSIONS); + record.getMetrics().get(METRIC_PREFIX + NUM_RELEASED_SESSIONS); assertThat(numReleasedSessions.size()).isEqualTo(1); assertThat(numReleasedSessions.get(0).value()).isEqualTo(0); assertThat(numReleasedSessions.get(0).keys()).isEqualTo(SPANNER_LABEL_KEYS); assertThat(numReleasedSessions.get(0).values()).isEqualTo(labelValues); List maxAllowedSessions = - record.getMetrics().get(MetricRegistryConstants.MAX_ALLOWED_SESSIONS); + record.getMetrics().get(METRIC_PREFIX + MAX_ALLOWED_SESSIONS); assertThat(maxAllowedSessions.size()).isEqualTo(1); assertThat(maxAllowedSessions.get(0).value()).isEqualTo(options.getMaxSessions()); assertThat(maxAllowedSessions.get(0).keys()).isEqualTo(SPANNER_LABEL_KEYS); assertThat(maxAllowedSessions.get(0).values()).isEqualTo(labelValues); List numSessionsInPool = - record.getMetrics().get(MetricRegistryConstants.NUM_SESSIONS_IN_POOL); + record.getMetrics().get(METRIC_PREFIX + NUM_SESSIONS_IN_POOL); assertThat(numSessionsInPool.size()).isEqualTo(4); PointWithFunction beingPrepared = numSessionsInPool.get(0); List labelValuesWithBeingPreparedType = new ArrayList<>(labelValues); @@ -1746,10 +1821,12 @@ public void testSessionMetrics() throws Exception { latch.await(); // Wait until the request has timed out. int waitCount = 0; - while (pool.getNumWaiterTimeouts() == 0L && waitCount < 1000) { - Thread.sleep(5L); + while (pool.getNumWaiterTimeouts() == 0L && waitCount < 5000) { + //noinspection BusyWait + Thread.sleep(1L); waitCount++; } + assertTrue(pool.getNumWaiterTimeouts() > 0L); // Return the checked out session to the pool so the async request will get a session and // finish. session2.close(); @@ -1758,19 +1835,19 @@ public void testSessionMetrics() throws Exception { executor.shutdown(); session1.close(); - numAcquiredSessions = record.getMetrics().get(MetricRegistryConstants.NUM_ACQUIRED_SESSIONS); + numAcquiredSessions = record.getMetrics().get(METRIC_PREFIX + NUM_ACQUIRED_SESSIONS); assertThat(numAcquiredSessions.size()).isEqualTo(1); assertThat(numAcquiredSessions.get(0).value()).isEqualTo(3L); - numReleasedSessions = record.getMetrics().get(MetricRegistryConstants.NUM_RELEASED_SESSIONS); + numReleasedSessions = record.getMetrics().get(METRIC_PREFIX + NUM_RELEASED_SESSIONS); assertThat(numReleasedSessions.size()).isEqualTo(1); assertThat(numReleasedSessions.get(0).value()).isEqualTo(3L); - maxInUseSessions = record.getMetrics().get(MetricRegistryConstants.MAX_IN_USE_SESSIONS); + maxInUseSessions = record.getMetrics().get(METRIC_PREFIX + MAX_IN_USE_SESSIONS); assertThat(maxInUseSessions.size()).isEqualTo(1); assertThat(maxInUseSessions.get(0).value()).isEqualTo(2L); - numSessionsInPool = record.getMetrics().get(MetricRegistryConstants.NUM_SESSIONS_IN_POOL); + numSessionsInPool = record.getMetrics().get(METRIC_PREFIX + NUM_SESSIONS_IN_POOL); assertThat(numSessionsInPool.size()).isEqualTo(4); beingPrepared = numSessionsInPool.get(0); assertThat(beingPrepared.value()).isEqualTo(0L); @@ -1782,6 +1859,191 @@ public void testSessionMetrics() throws Exception { assertThat(writePreparedSessions.value()).isEqualTo(0L); } + @Test + public void testOpenCensusMetricsDisable() { + SpannerOptions.disableOpenCensusMetrics(); + // Create a session pool with max 2 session and a low timeout for waiting for a session. + options = + SessionPoolOptions.newBuilder() + .setMinSessions(1) + .setMaxSessions(2) + .setMaxIdleSessions(0) + .setInitialWaitForSessionTimeoutMillis(50L) + .build(); + FakeClock clock = new FakeClock(); + clock.currentTimeMillis.set(System.currentTimeMillis()); + FakeMetricRegistry metricRegistry = new FakeMetricRegistry(); + List labelValues = + Arrays.asList( + LabelValue.create("client1"), + LabelValue.create("database1"), + LabelValue.create("instance1"), + LabelValue.create("1.0.0")); + + setupMockSessionCreation(); + pool = createPool(clock, metricRegistry, labelValues); + PooledSessionFuture session1 = pool.getSession(); + PooledSessionFuture session2 = pool.getSession(); + session1.get(); + session2.get(); + + MetricsRecord record = metricRegistry.pollRecord(); + assertThat(record.getMetrics().size()).isEqualTo(0); + SpannerOptions.enableOpenCensusMetrics(); + } + + @Test + public void testOpenTelemetrySessionMetrics() throws Exception { + SpannerOptions.resetActiveTracingFramework(); + SpannerOptions.enableOpenTelemetryMetrics(); + // Create a session pool with max 2 session and a low timeout for waiting for a session. + if (minSessions == 1) { + options = + SessionPoolOptions.newBuilder() + .setMinSessions(1) + .setMaxSessions(3) + .setMaxIdleSessions(0) + .setInitialWaitForSessionTimeoutMillis(50L) + .build(); + FakeClock clock = new FakeClock(); + clock.currentTimeMillis.set(System.currentTimeMillis()); + + InMemoryMetricReader inMemoryMetricReader = InMemoryMetricReader.create(); + SdkMeterProvider sdkMeterProvider = + SdkMeterProvider.builder().registerMetricReader(inMemoryMetricReader).build(); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build(); + + setupMockSessionCreation(); + + AttributesBuilder attributesBuilder = Attributes.builder(); + attributesBuilder.put("client_id", "testClient"); + attributesBuilder.put("database", "testDb"); + attributesBuilder.put("instance_id", "test_instance"); + attributesBuilder.put("library_version", "test_version"); + + pool = + createPool( + clock, + Metrics.getMetricRegistry(), + SPANNER_DEFAULT_LABEL_VALUES, + openTelemetry, + attributesBuilder.build()); + PooledSessionFuture session1 = pool.getSession(); + PooledSessionFuture session2 = pool.getSession(); + session1.get(); + session2.get(); + + Collection metricDataCollection = inMemoryMetricReader.collectAllMetrics(); + // Acquired sessions are 2. + verifyMetricData(metricDataCollection, NUM_ACQUIRED_SESSIONS, 1, 2L); + // Max in use session are 2. + verifyMetricData(metricDataCollection, MAX_IN_USE_SESSIONS, 1, 2D); + // Max Allowed sessions should be 3 + verifyMetricData(metricDataCollection, MAX_ALLOWED_SESSIONS, 1, 3D); + // Released sessions should be 0 + verifyMetricData(metricDataCollection, NUM_RELEASED_SESSIONS, 1, 0L); + // Num sessions in pool + verifyMetricData(metricDataCollection, NUM_SESSIONS_IN_POOL, 1, NUM_SESSIONS_IN_USE, 2); + + PooledSessionFuture session3 = pool.getSession(); + session3.get(); + + final CountDownLatch latch = new CountDownLatch(1); + // Try asynchronously to take another session. This attempt should time out. + Future fut = + executor.submit( + () -> { + latch.countDown(); + Session session = pool.getSession(); + session.close(); + return null; + }); + // Wait until the background thread is actually waiting for a session. + latch.await(); + // Wait until the request has timed out. + int waitCount = 0; + while (pool.getNumWaiterTimeouts() == 0L && waitCount < 1000) { + Thread.sleep(5L); + waitCount++; + } + // Return the checked out session to the pool so the async request will get a session and + // finish. + session2.close(); + // Verify that the async request also succeeds. + fut.get(10L, TimeUnit.SECONDS); + executor.shutdown(); + + metricDataCollection = inMemoryMetricReader.collectAllMetrics(); + + // Max Allowed sessions should be 3 + verifyMetricData(metricDataCollection, MAX_ALLOWED_SESSIONS, 1, 3D); + // Session timeouts 1 + // verifyMetricData(metricDataCollection, GET_SESSION_TIMEOUTS, 1, 1L); + // Max in use session are 2. + verifyMetricData(metricDataCollection, MAX_IN_USE_SESSIONS, 1, 3D); + // Session released 2 + verifyMetricData(metricDataCollection, NUM_RELEASED_SESSIONS, 1, 2L); + // Acquired sessions are 4. + verifyMetricData(metricDataCollection, NUM_ACQUIRED_SESSIONS, 1, 4L); + // Num sessions in pool + verifyMetricData(metricDataCollection, NUM_SESSIONS_IN_POOL, 1, NUM_SESSIONS_IN_USE, 2); + verifyMetricData(metricDataCollection, NUM_SESSIONS_IN_POOL, 1, NUM_SESSIONS_AVAILABLE, 1); + } + } + + private static void verifyMetricData( + Collection metricDataCollection, String metricName, int size, long value) { + Collection metricDataFiltered = + metricDataCollection.stream() + .filter(x -> x.getName().equals(metricName)) + .collect(Collectors.toList()); + + assertEquals(metricDataFiltered.size(), size); + MetricData metricData = metricDataFiltered.stream().findFirst().get(); + assertEquals( + metricData.getLongSumData().getPoints().stream().findFirst().get().getValue(), value); + } + + private static void verifyMetricData( + Collection metricDataCollection, String metricName, int size, double value) { + Collection metricDataFiltered = + metricDataCollection.stream() + .filter(x -> x.getName().equals(metricName)) + .collect(Collectors.toList()); + + assertEquals(metricDataFiltered.size(), size); + MetricData metricData = metricDataFiltered.stream().findFirst().get(); + assertEquals( + metricData.getDoubleGaugeData().getPoints().stream().findFirst().get().getValue(), + value, + 0.0); + } + + private static void verifyMetricData( + Collection metricDataCollection, + String metricName, + int size, + String labelName, + long value) { + Collection metricDataFiltered = + metricDataCollection.stream() + .filter(x -> x.getName().equals(metricName)) + .collect(Collectors.toList()); + + assertEquals(metricDataFiltered.size(), size); + + MetricData metricData = metricDataFiltered.stream().findFirst().get(); + + assertEquals( + metricData.getLongSumData().getPoints().stream() + .filter(x -> x.getAttributes().asMap().containsValue(labelName)) + .findFirst() + .get() + .getValue(), + value); + } + @Test public void testGetDatabaseRole() throws Exception { setupMockSessionCreation(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpanTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpanTest.java index 4d13829a857..672827318d1 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpanTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpanTest.java @@ -25,6 +25,7 @@ import com.google.cloud.NoCredentials; import com.google.cloud.spanner.MockSpannerServiceImpl.SimulatedExecutionTime; import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; +import com.google.common.collect.ImmutableList; import com.google.protobuf.ListValue; import com.google.spanner.v1.ResultSetMetadata; import com.google.spanner.v1.StructType; @@ -35,9 +36,19 @@ import io.grpc.StatusRuntimeException; import io.grpc.inprocess.InProcessServerBuilder; import io.opencensus.trace.Tracing; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; import java.lang.reflect.Modifier; +import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.stream.Collectors; import org.junit.After; import org.junit.AfterClass; import org.junit.Assume; @@ -90,6 +101,9 @@ public class SpanTest { private DatabaseClient client; private Spanner spannerWithTimeout; private DatabaseClient clientWithTimeout; + + private static InMemorySpanExporter openTelemetrySpanExporter; + private static FailOnOverkillTraceComponentImpl failOnOverkillTraceComponent = new FailOnOverkillTraceComponentImpl(); @@ -152,13 +166,36 @@ public static void stopServer() throws InterruptedException { } } + @BeforeClass + public static void setupOpenTelemetry() { + SpannerOptions.resetActiveTracingFramework(); + SpannerOptions.enableOpenCensusTraces(); + } + @Before public void setUp() throws Exception { + // Incorporating OpenTelemetry configuration to ensure that OpenCensus traces are utilized by + // default, + // regardless of the presence of OpenTelemetry configuration. + openTelemetrySpanExporter = InMemorySpanExporter.create(); + SdkTracerProvider tracerProvider = + SdkTracerProvider.builder() + .addSpanProcessor(SimpleSpanProcessor.create(openTelemetrySpanExporter)) + .build(); + + GlobalOpenTelemetry.resetForTest(); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder() + .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())) + .setTracerProvider(tracerProvider) + .build(); + SpannerOptions.Builder builder = SpannerOptions.newBuilder() .setProjectId(TEST_PROJECT) .setChannelProvider(channelProvider) .setCredentials(NoCredentials.getInstance()) + .setOpenTelemetry(openTelemetry) .setSessionPoolOption(SessionPoolOptions.newBuilder().setMinSessions(0).build()); spanner = builder.build().getService(); @@ -206,6 +243,7 @@ public void setUp() throws Exception { DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); failOnOverkillTraceComponent.clearSpans(); + failOnOverkillTraceComponent.clearAnnotations(); } @After @@ -240,12 +278,30 @@ public void singleUse() { // Just consume the result set. } } + + // OpenTelemetry spans should be 0 as OpenCensus is default enabled. + assertEquals(openTelemetrySpanExporter.getFinishedSpanItems().size(), 0); + + // OpenCensus spans and events verification Map spans = failOnOverkillTraceComponent.getSpans(); assertThat(spans).containsEntry("CloudSpanner.ReadOnlyTransaction", true); assertThat(spans).containsEntry("CloudSpannerOperation.BatchCreateSessions", true); assertThat(spans).containsEntry("SessionPool.WaitForSession", true); assertThat(spans).containsEntry("CloudSpannerOperation.BatchCreateSessionsRequest", true); assertThat(spans).containsEntry("CloudSpannerOperation.ExecuteStreamingQuery", true); + + List expectedAnnotations = + ImmutableList.of( + "Requesting 25 sessions", + "Request for 25 sessions returned 25 sessions", + "Creating 25 sessions", + "Acquiring session", + "No session available", + "Creating sessions", + "Waiting for a session to come available", + "Using Session", + "Starting/Resuming stream"); + verifyAnnotations(failOnOverkillTraceComponent.getAnnotations(), expectedAnnotations); } @Test @@ -264,6 +320,21 @@ public void multiUse() { assertThat(spans).containsEntry("SessionPool.WaitForSession", true); assertThat(spans).containsEntry("CloudSpannerOperation.BatchCreateSessionsRequest", true); assertThat(spans).containsEntry("CloudSpannerOperation.ExecuteStreamingQuery", true); + + List expectedAnnotations = + ImmutableList.of( + "Requesting 25 sessions", + "Request for 25 sessions returned 25 sessions", + "Creating 25 sessions", + "Acquiring session", + "No session available", + "Creating sessions", + "Waiting for a session to come available", + "Using Session", + "Starting/Resuming stream", + "Creating Transaction", + "Transaction Creation Done"); + verifyAnnotations(failOnOverkillTraceComponent.getAnnotations(), expectedAnnotations); } @Test @@ -276,6 +347,22 @@ public void transactionRunner() { assertThat(spans).containsEntry("SessionPool.WaitForSession", true); assertThat(spans).containsEntry("CloudSpannerOperation.BatchCreateSessionsRequest", true); assertThat(spans).containsEntry("CloudSpannerOperation.Commit", true); + + List expectedAnnotations = + ImmutableList.of( + "Acquiring session", + "No session available", + "Creating sessions", + "Waiting for a session to come available", + "Using Session", + "Starting Transaction Attempt", + "Starting Commit", + "Commit Done", + "Transaction Attempt Succeeded", + "Requesting 25 sessions", + "Request for 25 sessions returned 25 sessions", + "Creating 25 sessions"); + verifyAnnotations(expectedAnnotations, failOnOverkillTraceComponent.getAnnotations()); } @Test @@ -293,5 +380,26 @@ public void transactionRunnerWithError() { assertThat(spans).containsEntry("CloudSpannerOperation.BatchCreateSessions", true); assertThat(spans).containsEntry("SessionPool.WaitForSession", true); assertThat(spans).containsEntry("CloudSpannerOperation.BatchCreateSessionsRequest", true); + + List expectedAnnotations = + ImmutableList.of( + "Acquiring session", + "No session available", + "Creating sessions", + "Waiting for a session to come available", + "Using Session", + "Starting Transaction Attempt", + "Transaction Attempt Failed in user operation", + "Requesting 25 sessions", + "Request for 25 sessions returned 25 sessions", + "Creating 25 sessions"); + + verifyAnnotations(expectedAnnotations, failOnOverkillTraceComponent.getAnnotations()); + } + + private void verifyAnnotations(List actualAnnotations, List expectedAnnotations) { + assertEquals( + actualAnnotations.stream().distinct().sorted().collect(Collectors.toList()), + expectedAnnotations.stream().sorted().collect(Collectors.toList())); } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerImplTest.java index 2d098d97004..31a6cad4c8a 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerImplTest.java @@ -31,6 +31,7 @@ import com.google.cloud.spanner.SpannerImpl.ClosedException; import com.google.cloud.spanner.spi.v1.SpannerRpc; import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions; +import io.opentelemetry.api.OpenTelemetry; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Collections; @@ -39,6 +40,7 @@ import java.util.UUID; 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; @@ -57,6 +59,12 @@ public class SpannerImplTest { @Captor ArgumentCaptor> options; + @BeforeClass + public static void setupOpenTelemetry() { + SpannerOptions.resetActiveTracingFramework(); + SpannerOptions.enableOpenTelemetryTraces(); + } + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -66,6 +74,7 @@ public void setUp() { when(spannerOptions.getRetrySettings()).thenReturn(RetrySettings.newBuilder().build()); when(spannerOptions.getClock()).thenReturn(NanoClock.getDefaultClock()); when(spannerOptions.getSessionLabels()).thenReturn(Collections.emptyMap()); + when(spannerOptions.getOpenTelemetry()).thenReturn(OpenTelemetry.noop()); impl = new SpannerImpl(rpc, spannerOptions); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerOptionsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerOptionsTest.java index 4e41660bca1..522cd0147a3 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerOptionsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerOptionsTest.java @@ -61,6 +61,11 @@ import com.google.spanner.v1.ReadRequest; import com.google.spanner.v1.RollbackRequest; import com.google.spanner.v1.SpannerGrpc; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -117,17 +122,24 @@ public void builder() { String projectId = "test-project"; Map labels = new HashMap<>(); labels.put("env", "dev"); + InMemoryMetricReader inMemoryMetricReader = InMemoryMetricReader.create(); + SdkMeterProvider sdkMeterProvider = + SdkMeterProvider.builder().registerMetricReader(inMemoryMetricReader).build(); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build(); SpannerOptions options = SpannerOptions.newBuilder() .setHost(host) .setProjectId(projectId) .setPrefetchChunks(2) .setSessionLabels(labels) + .setOpenTelemetry(openTelemetry) .build(); assertThat(options.getHost()).isEqualTo(host); assertThat(options.getProjectId()).isEqualTo(projectId); assertThat(options.getPrefetchChunks()).isEqualTo(2); assertThat(options.getSessionLabels()).containsExactlyEntriesIn(labels); + assertThat(options.getOpenTelemetry()).isEqualTo(openTelemetry); } @Test @@ -1101,4 +1113,19 @@ public void checkCreatedInstanceWhenGrpcGcpExtensionEnabled() { spanner1.close(); spanner2.close(); } + + @Test + public void checkGlobalOpenTelemetryWhenNotInjected() { + GlobalOpenTelemetry.resetForTest(); + InMemoryMetricReader inMemoryMetricReader = InMemoryMetricReader.create(); + SdkMeterProvider sdkMeterProvider = + SdkMeterProvider.builder().registerMetricReader(inMemoryMetricReader).build(); + OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).buildAndRegisterGlobal(); + SpannerOptions options = + SpannerOptions.newBuilder() + .setProjectId("test-project") + .setCredentials(NoCredentials.getInstance()) + .build(); + assertEquals(GlobalOpenTelemetry.get(), options.getOpenTelemetry()); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionContextImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionContextImplTest.java index 0cb2b5d29ae..53120eda441 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionContextImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionContextImplTest.java @@ -19,6 +19,7 @@ import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyMap; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -49,6 +50,9 @@ public class TransactionContextImplTest { @Mock private SessionImpl session; + @Mock private ISpan span; + @Mock private TraceWrapper tracer; + @SuppressWarnings("unchecked") @Before public void setup() { @@ -60,12 +64,18 @@ public void setup() { .setCommitTimestamp(Timestamp.newBuilder().setSeconds(99L).setNanos(10).build()) .build())); when(session.getName()).thenReturn("test"); + doNothing().when(span).setStatus(any(Throwable.class)); + doNothing().when(span).end(); + doNothing().when(span).addAnnotation("Starting Commit"); + when(tracer.spanBuilderWithExplicitParent(SpannerImpl.COMMIT, span)).thenReturn(span); } private TransactionContextImpl createContext() { return TransactionContextImpl.newBuilder() .setSession(session) .setRpc(rpc) + .setSpan(span) + .setTracer(tracer) .setTransactionId(ByteString.copyFromUtf8("test")) .setOptions(Options.fromTransactionOptions()) .build(); @@ -165,6 +175,8 @@ public void testReturnCommitStats() { TransactionContextImpl.newBuilder() .setSession(session) .setRpc(rpc) + .setSpan(span) + .setTracer(tracer) .setTransactionId(transactionId) .setOptions(Options.fromTransactionOptions(Options.commitStats())) .build()) { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java index f8462693fe6..dc28b333c4f 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; @@ -45,13 +46,14 @@ import com.google.spanner.v1.ResultSetStats; import com.google.spanner.v1.Session; import com.google.spanner.v1.Transaction; -import io.opencensus.trace.Span; +import io.opentelemetry.api.OpenTelemetry; import java.util.Collections; import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -77,10 +79,20 @@ public void release(ScheduledExecutorService exec) { @Mock TransactionRunnerImpl.TransactionContextImpl txn; private TransactionManagerImpl manager; + @BeforeClass + public static void setupOpenTelemetry() { + SpannerOptions.resetActiveTracingFramework(); + SpannerOptions.enableOpenTelemetryTraces(); + } + @Before public void setUp() { initMocks(this); - manager = new TransactionManagerImpl(session, mock(Span.class)); + manager = + new TransactionManagerImpl( + session, + new OpenTelemetrySpan(mock(io.opentelemetry.api.trace.Span.class)), + mock(TraceWrapper.class)); } @Test @@ -200,6 +212,7 @@ public void usesPreparedTransaction() { when(options.getSessionPoolOptions()).thenReturn(sessionPoolOptions); when(options.getSessionLabels()).thenReturn(Collections.emptyMap()); when(options.getDatabaseRole()).thenReturn("role"); + when(options.getOpenTelemetry()).thenReturn(OpenTelemetry.noop()); SpannerRpc rpc = mock(SpannerRpc.class); when(rpc.asyncDeleteSession(Mockito.anyString(), Mockito.anyMap())) .thenReturn(ApiFutures.immediateFuture(Empty.getDefaultInstance())); @@ -262,6 +275,7 @@ public void inlineBegin() { when(options.getSessionLabels()).thenReturn(Collections.emptyMap()); when(options.getDefaultQueryOptions(Mockito.any(DatabaseId.class))) .thenReturn(QueryOptions.getDefaultInstance()); + when(options.getOpenTelemetry()).thenReturn(OpenTelemetry.noop()); SpannerRpc rpc = mock(SpannerRpc.class); when(rpc.asyncDeleteSession(Mockito.anyString(), Mockito.anyMap())) .thenReturn(ApiFutures.immediateFuture(Empty.getDefaultInstance())); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java index efc57fb480b..cf5f901f65e 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; @@ -58,7 +59,10 @@ import io.grpc.Status; import io.grpc.StatusRuntimeException; import io.grpc.protobuf.ProtoUtils; -import io.opencensus.trace.Span; +import io.opencensus.trace.Tracing; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Scope; import java.util.Arrays; import java.util.Collections; import java.util.UUID; @@ -66,6 +70,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -95,12 +100,22 @@ public void release(ScheduledExecutorService exec) { private TransactionRunnerImpl transactionRunner; private boolean firstRun; private boolean usedInlinedBegin; + private TraceWrapper tracer; + private ISpan span; + + @BeforeClass + public static void setupOpenTelemetry() { + SpannerOptions.resetActiveTracingFramework(); + SpannerOptions.enableOpenTelemetryTraces(); + } @Before public void setUp() { MockitoAnnotations.initMocks(this); + tracer = new TraceWrapper(Tracing.getTracer(), OpenTelemetry.noop().getTracer("")); firstRun = true; when(session.newTransaction(Options.fromTransactionOptions())).thenReturn(txn); + when(session.getTracer()).thenReturn(tracer); when(rpc.executeQuery(Mockito.any(ExecuteSqlRequest.class), Mockito.anyMap(), eq(true))) .thenAnswer( invocation -> { @@ -128,7 +143,10 @@ public void setUp() { .build())); when(rpc.rollbackAsync(Mockito.any(RollbackRequest.class), Mockito.anyMap())) .thenReturn(ApiFutures.immediateFuture(Empty.getDefaultInstance())); - transactionRunner.setSpan(mock(Span.class)); + Span oTspan = mock(Span.class); + span = new OpenTelemetrySpan(oTspan); + when(oTspan.makeCurrent()).thenReturn(mock(Scope.class)); + transactionRunner.setSpan(span); } @SuppressWarnings("unchecked") @@ -144,6 +162,7 @@ public void usesPreparedTransaction() { when(options.getSessionPoolOptions()).thenReturn(sessionPoolOptions); when(options.getSessionLabels()).thenReturn(Collections.emptyMap()); when(options.getDatabaseRole()).thenReturn("role"); + when(options.getOpenTelemetry()).thenReturn(OpenTelemetry.noop()); SpannerRpc rpc = mock(SpannerRpc.class); when(rpc.asyncDeleteSession(Mockito.anyString(), Mockito.anyMap())) .thenReturn(ApiFutures.immediateFuture(Empty.getDefaultInstance())); @@ -279,6 +298,7 @@ public void inlineBegin() { when(spanner.getDefaultQueryOptions(Mockito.any(DatabaseId.class))) .thenReturn(QueryOptions.getDefaultInstance()); when(spanner.getOptions()).thenReturn(options); + when(spanner.getTracer()).thenReturn(tracer); SessionPoolOptions sessionPoolOptions = SessionPoolOptions.newBuilder().build(); when(options.getSessionPoolOptions()).thenReturn(sessionPoolOptions); @@ -292,9 +312,9 @@ public void prepareReadWriteTransaction() { throw new IllegalStateException(); } }; - session.setCurrentSpan(mock(Span.class)); + session.setCurrentSpan(new OpenTelemetrySpan(mock(io.opentelemetry.api.trace.Span.class))); TransactionRunnerImpl runner = new TransactionRunnerImpl(session); - runner.setSpan(mock(Span.class)); + runner.setSpan(span); assertThat(usedInlinedBegin).isFalse(); runner.run( transaction -> { @@ -318,6 +338,8 @@ private long[] batchDmlException(int status) { .setTransactionId(ByteString.copyFromUtf8(UUID.randomUUID().toString())) .setOptions(Options.fromTransactionOptions()) .setRpc(rpc) + .setTracer(session.getTracer()) + .setSpan(session.getTracer().getCurrentSpan()) .build(); when(session.newTransaction(Options.fromTransactionOptions())).thenReturn(transaction); when(session.beginTransactionAsync(true)) @@ -325,7 +347,7 @@ private long[] batchDmlException(int status) { ApiFutures.immediateFuture(ByteString.copyFromUtf8(UUID.randomUUID().toString()))); when(session.getName()).thenReturn(SessionId.of("p", "i", "d", "test").getName()); TransactionRunnerImpl runner = new TransactionRunnerImpl(session); - runner.setSpan(mock(Span.class)); + runner.setSpan(span); ExecuteBatchDmlResponse response1 = ExecuteBatchDmlResponse.newBuilder() .addResultSets( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java index 43c57c8e21c..af67859b4a8 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java @@ -36,6 +36,7 @@ import com.google.protobuf.ListValue; import com.google.protobuf.Value; import com.google.spanner.v1.ExecuteSqlRequest; +import com.google.spanner.v1.ResultSet; import com.google.spanner.v1.ResultSetMetadata; import com.google.spanner.v1.ResultSetStats; import com.google.spanner.v1.StructType; @@ -66,7 +67,10 @@ public abstract class AbstractMockServerTest { public static final long COUNT_AFTER_INSERT = 1L; public static final Statement SELECT_COUNT_STATEMENT = Statement.of("SELECT COUNT(*) AS C FROM TEST WHERE ID=1"); - private static final ResultSetMetadata SELECT_COUNT_METADATA = + + protected static final Statement SELECT1_STATEMENT = Statement.of("SELECT 1"); + + private static final ResultSetMetadata SINGLE_COL_INT64_RESULTSET_METADATA = ResultSetMetadata.newBuilder() .setRowType( StructType.newBuilder() @@ -86,7 +90,7 @@ public abstract class AbstractMockServerTest { .setStringValue(String.valueOf(COUNT_BEFORE_INSERT)) .build()) .build()) - .setMetadata(SELECT_COUNT_METADATA) + .setMetadata(SINGLE_COL_INT64_RESULTSET_METADATA) .build(); public static final com.google.spanner.v1.ResultSet SELECT_COUNT_RESULTSET_AFTER_INSERT = com.google.spanner.v1.ResultSet.newBuilder() @@ -95,7 +99,7 @@ public abstract class AbstractMockServerTest { .addValues( Value.newBuilder().setStringValue(String.valueOf(COUNT_AFTER_INSERT)).build()) .build()) - .setMetadata(SELECT_COUNT_METADATA) + .setMetadata(SINGLE_COL_INT64_RESULTSET_METADATA) .build(); public static final com.google.spanner.v1.ResultSet UPDATE_RETURNING_RESULTSET = com.google.spanner.v1.ResultSet.newBuilder() @@ -110,6 +114,16 @@ public abstract class AbstractMockServerTest { .setType(Type.newBuilder().setCodeValue(TypeCode.INT64_VALUE)) .build()))) .build(); + + protected static final ResultSet SELECT1_RESULTSET = + ResultSet.newBuilder() + .setMetadata(SINGLE_COL_INT64_RESULTSET_METADATA) + .addRows( + ListValue.newBuilder() + .addValues(Value.newBuilder().setStringValue("1").build()) + .build()) + .build(); + public static final Statement INSERT_STATEMENT = Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test aborted')"); public static final Statement INSERT_RETURNING_STATEMENT = @@ -173,6 +187,7 @@ public void getOperation( StatementResult.updateReturning(PG_INSERT_RETURNING_STATEMENT, UPDATE_RETURNING_RESULTSET)); mockSpanner.putStatementResult( StatementResult.query(SELECT_RANDOM_STATEMENT, RANDOM_RESULT_SET)); + mockSpanner.putStatementResult(StatementResult.query(SELECT1_STATEMENT, SELECT1_RESULTSET)); futureParentHandlers = Logger.getLogger(AbstractFuture.class.getName()).getUseParentHandlers(); exceptionRunnableParentHandlers = diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ClientSideStatementsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ClientSideStatementsTest.java index c0b1e3a58dd..51503fd456a 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ClientSideStatementsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ClientSideStatementsTest.java @@ -167,8 +167,10 @@ private static void generateTestStatements( AbstractStatementParser parser, ClientSideStatementImpl statement) { for (String sql : statement.getExampleStatements()) { log(statement.getExamplePrerequisiteStatements(), sql); - if (statement.getStatementType() != ClientSideStatementType.RUN_PARTITION) { + if (statement.getStatementType() != ClientSideStatementType.RUN_PARTITION + && statement.getStatementType() != ClientSideStatementType.SET_DIRECTED_READ) { // Partition ids are case-sensitive. + // DirectedReadOptions are case-sensitive. log(statement.getExamplePrerequisiteStatements(), upper(sql)); log(statement.getExamplePrerequisiteStatements(), lower(sql)); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectedReadOptionsUtilTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectedReadOptionsUtilTest.java new file mode 100644 index 00000000000..0669cb5aad6 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectedReadOptionsUtilTest.java @@ -0,0 +1,77 @@ +/* + * Copyright 2024 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.connection; + +import static junit.framework.TestCase.assertEquals; + +import com.google.spanner.v1.DirectedReadOptions; +import com.google.spanner.v1.DirectedReadOptions.ExcludeReplicas; +import com.google.spanner.v1.DirectedReadOptions.IncludeReplicas; +import com.google.spanner.v1.DirectedReadOptions.ReplicaSelection; +import com.google.spanner.v1.DirectedReadOptions.ReplicaSelection.Type; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests converting {@link DirectedReadOptions} to/from JSON. The test cases here are not very + * extensive, as it a very simple wrapper around the {@link com.google.protobuf.util.JsonFormat} + * class, which has its own test suite. The tests in this class only serve as a simple verification + * that the formatter works as expected. + */ +@RunWith(JUnit4.class) +public class DirectedReadOptionsUtilTest { + + @Test + public void testToString() { + assertRoundTrip("", DirectedReadOptions.newBuilder().build()); + assertRoundTrip( + "{\"includeReplicas\":{}}", + DirectedReadOptions.newBuilder() + .setIncludeReplicas(IncludeReplicas.newBuilder().build()) + .build()); + assertRoundTrip( + "{\"includeReplicas\":{\"replicaSelections\":[{\"location\":\"eu-west1\",\"type\":\"READ_ONLY\"}]}}", + DirectedReadOptions.newBuilder() + .setIncludeReplicas( + IncludeReplicas.newBuilder() + .addReplicaSelections( + ReplicaSelection.newBuilder() + .setType(Type.READ_ONLY) + .setLocation("eu-west1") + .build()) + .build()) + .build()); + assertRoundTrip( + "{\"excludeReplicas\":{\"replicaSelections\":[{\"location\":\"eu-west1\",\"type\":\"READ_ONLY\"}]}}", + DirectedReadOptions.newBuilder() + .setExcludeReplicas( + ExcludeReplicas.newBuilder() + .addReplicaSelections( + ReplicaSelection.newBuilder() + .setType(Type.READ_ONLY) + .setLocation("eu-west1") + .build()) + .build()) + .build()); + } + + private void assertRoundTrip(String json, DirectedReadOptions options) { + assertEquals(json, DirectedReadOptionsUtil.toString(options)); + assertEquals(options, DirectedReadOptionsUtil.parse(json)); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectedReadTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectedReadTest.java new file mode 100644 index 00000000000..099c5d10478 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectedReadTest.java @@ -0,0 +1,317 @@ +/* + * Copyright 2024 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.connection; + +import static junit.framework.TestCase.assertEquals; + +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.MockSpannerServiceImpl; +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.Statement; +import com.google.common.collect.ImmutableList; +import com.google.protobuf.ListValue; +import com.google.protobuf.Value; +import com.google.spanner.v1.*; +import com.google.spanner.v1.DirectedReadOptions.ExcludeReplicas; +import com.google.spanner.v1.DirectedReadOptions.IncludeReplicas; +import com.google.spanner.v1.DirectedReadOptions.ReplicaSelection; +import com.google.spanner.v1.StructType.Field; +import java.util.Collection; +import java.util.List; +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.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class DirectedReadTest extends AbstractMockServerTest { + private static final Statement READ_STATEMENT = Statement.of("SELECT 1 AS C"); + + private static final Statement GOOGLESQL_DML_STATEMENT = + Statement.of("INSERT INTO T (id) VALUES (1) THEN RETURN ID"); + private static final Statement POSTGRESQL_DML_STATEMENT = + Statement.of("INSERT INTO T (id) VALUES (1) RETURNING ID"); + + @Parameters(name = "dialect = {0}") + public static Collection data() { + ImmutableList.Builder builder = ImmutableList.builder(); + for (Dialect dialect : Dialect.values()) { + builder.add(new Object[] {dialect}); + } + return builder.build(); + } + + @Parameter public Dialect dialect; + + private Dialect currentDialect; + + @BeforeClass + public static void setupQueryResults() { + com.google.spanner.v1.ResultSet resultSet = + com.google.spanner.v1.ResultSet.newBuilder() + .setMetadata( + ResultSetMetadata.newBuilder() + .setRowType( + StructType.newBuilder() + .addFields( + Field.newBuilder() + .setName("C") + .setType(Type.newBuilder().setCode(TypeCode.INT64).build()) + .build()) + .build()) + .build()) + .addRows( + ListValue.newBuilder() + .addValues(Value.newBuilder().setStringValue("1").build()) + .build()) + .build(); + mockSpanner.putStatementResult( + MockSpannerServiceImpl.StatementResult.query(READ_STATEMENT, resultSet)); + mockSpanner.putStatementResult( + MockSpannerServiceImpl.StatementResult.query( + GOOGLESQL_DML_STATEMENT, + resultSet + .toBuilder() + .setStats(ResultSetStats.newBuilder().setRowCountExact(1L).build()) + .build())); + mockSpanner.putStatementResult( + MockSpannerServiceImpl.StatementResult.query( + POSTGRESQL_DML_STATEMENT, + resultSet + .toBuilder() + .setStats(ResultSetStats.newBuilder().setRowCountExact(1L).build()) + .build())); + } + + @Before + public void setupDialect() { + if (currentDialect != dialect) { + // Reset the dialect result. + SpannerPool.closeSpannerPool(); + mockSpanner.putStatementResult( + MockSpannerServiceImpl.StatementResult.detectDialectResult(dialect)); + currentDialect = dialect; + } + } + + private String getVariablePrefix() { + return dialect == Dialect.POSTGRESQL ? "spanner." : ""; + } + + private Statement getDmlStatement() { + return dialect == Dialect.POSTGRESQL ? POSTGRESQL_DML_STATEMENT : GOOGLESQL_DML_STATEMENT; + } + + @After + public void clearRequests() { + mockSpanner.clearRequests(); + } + + @Test + public void testNoDirectedReadByDefault() { + try (Connection connection = createConnection()) { + for (boolean readOnly : new boolean[] {true, false}) { + for (boolean autocommit : new boolean[] {true, false}) { + connection.setAutocommit(autocommit); + connection.setReadOnly(readOnly); + + executeReadQuery(connection); + assertDirectedReadOptions(DirectedReadOptions.getDefaultInstance()); + + if (!autocommit) { + connection.commit(); + } + mockSpanner.clearRequests(); + } + } + } + } + + @Test + public void testSetDirectedRead() { + for (DirectedReadOptions expected : + new DirectedReadOptions[] { + DirectedReadOptions.newBuilder() + .setIncludeReplicas( + IncludeReplicas.newBuilder() + .addReplicaSelections( + ReplicaSelection.newBuilder() + .setLocation("eu-west1") + .setType(ReplicaSelection.Type.READ_ONLY) + .build()) + .build()) + .build(), + DirectedReadOptions.newBuilder() + .setExcludeReplicas( + ExcludeReplicas.newBuilder() + .addReplicaSelections( + ReplicaSelection.newBuilder() + .setLocation("eu-west1") + .setType(ReplicaSelection.Type.READ_ONLY) + .build()) + .build()) + .build(), + DirectedReadOptions.newBuilder().build() + }) { + try (Connection connection = createConnection()) { + connection.setAutocommit(true); + connection.execute( + Statement.of( + String.format( + "set %sdirected_read='%s'", + getVariablePrefix(), DirectedReadOptionsUtil.toString(expected)))); + Repeat.twice( + () -> { + executeReadQuery(connection); + assertDirectedReadOptions(expected); + mockSpanner.clearRequests(); + }); + + // Reset to default. + connection.execute( + Statement.of(String.format("set %sdirected_read=''", getVariablePrefix()))); + executeReadQuery(connection); + assertDirectedReadOptions(DirectedReadOptions.getDefaultInstance()); + + mockSpanner.clearRequests(); + } + } + } + + @Test + public void testDirectedReadIsIgnoredForDmlInAutoCommit() { + DirectedReadOptions options = + DirectedReadOptions.newBuilder() + .setExcludeReplicas( + ExcludeReplicas.newBuilder() + .addReplicaSelections( + ReplicaSelection.newBuilder() + .setLocation("eu-west1") + .setType(ReplicaSelection.Type.READ_ONLY) + .build()) + .build()) + .build(); + + try (Connection connection = createConnection()) { + connection.setAutocommit(true); + connection.execute( + Statement.of( + String.format( + "set %sdirected_read='%s'", + getVariablePrefix(), DirectedReadOptionsUtil.toString(options)))); + // DML should not use directed read. + executeDmlQuery(connection); + assertDirectedReadOptions(DirectedReadOptions.getDefaultInstance()); + } + } + + @Test + public void testDirectedReadIsIgnoredInReadWriteTransaction() { + try (Connection connection = createConnection()) { + connection.setAutocommit(false); + connection.setReadOnly(false); + + for (Statement statement : new Statement[] {READ_STATEMENT, getDmlStatement()}) { + connection.execute( + Statement.of( + String.format( + "set %sdirected_read='%s'", + getVariablePrefix(), + DirectedReadOptionsUtil.toString( + DirectedReadOptions.newBuilder() + .setIncludeReplicas( + IncludeReplicas.newBuilder() + .addReplicaSelections( + ReplicaSelection.newBuilder() + .setType(ReplicaSelection.Type.READ_WRITE) + .setLocation("us-west1") + .build()) + .build()) + .build())))); + // This uses a read/write transaction, which will ignore any DirectedReadOptions. + executeQuery(connection, statement); + assertDirectedReadOptions(DirectedReadOptions.getDefaultInstance()); + + connection.commit(); + mockSpanner.clearRequests(); + } + } + } + + @Test + public void testDirectedReadIsUsedInReadOnlyTransaction() { + DirectedReadOptions expected = + DirectedReadOptions.newBuilder() + .setIncludeReplicas( + IncludeReplicas.newBuilder() + .addReplicaSelections( + ReplicaSelection.newBuilder() + .setType(ReplicaSelection.Type.READ_WRITE) + .setLocation("us-west1") + .build()) + .build()) + .build(); + + try (Connection connection = createConnection()) { + connection.setAutocommit(false); + connection.setReadOnly(true); + + connection.execute( + Statement.of( + String.format( + "set %sdirected_read='%s'", + getVariablePrefix(), DirectedReadOptionsUtil.toString(expected)))); + // This uses a read-only transaction, which will use the DirectedReadOptions. + // Repeatedly executing a query on the same connection will also continue to use the + // DirectedReadOptions that have been set for the connection. + Repeat.twice( + () -> { + executeReadQuery(connection); + assertDirectedReadOptions(expected); + mockSpanner.clearRequests(); + }); + } + } + + private void executeReadQuery(Connection connection) { + executeQuery(connection, READ_STATEMENT); + } + + private void executeDmlQuery(Connection connection) { + executeQuery(connection, getDmlStatement()); + } + + private void executeQuery(Connection connection, Statement statement) { + try (ResultSet resultSet = connection.executeQuery(statement)) { + //noinspection StatementWithEmptyBody + while (resultSet.next()) { + // ignore + } + } + } + + private void assertDirectedReadOptions(DirectedReadOptions expected) { + List requests = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class); + assertEquals(1, requests.size()); + ExecuteSqlRequest request = requests.get(0); + assertEquals(expected, request.getDirectedReadOptions()); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/QueryOptionsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/QueryOptionsTest.java new file mode 100644 index 00000000000..627e8c7acc9 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/QueryOptionsTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2024 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.connection; + +import static org.junit.Assert.assertEquals; + +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.Statement; +import com.google.spanner.v1.ExecuteSqlRequest; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class QueryOptionsTest extends AbstractMockServerTest { + + @Test + public void testUseOptimizerVersionFromConnectionUrl() { + try (Connection connection = createConnection("?optimizerVersion=10")) { + Repeat.twice( + () -> { + executeSelect1AndConsumeResults(connection); + + assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)); + ExecuteSqlRequest request = + mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0); + assertEquals("10", request.getQueryOptions().getOptimizerVersion()); + + mockSpanner.clearRequests(); + }); + } + } + + @Test + public void testUseOptimizerVersionFromStatement() { + try (Connection connection = createConnection()) { + connection.execute(Statement.of("set optimizer_version='7'")); + + Repeat.twice( + () -> { + executeSelect1AndConsumeResults(connection); + + assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)); + ExecuteSqlRequest request = + mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0); + assertEquals("7", request.getQueryOptions().getOptimizerVersion()); + + mockSpanner.clearRequests(); + }); + } + } + + private void executeSelect1AndConsumeResults(Connection connection) { + try (ResultSet resultSet = connection.executeQuery(SELECT1_STATEMENT)) { + //noinspection StatementWithEmptyBody + while (resultSet.next()) { + // ignore + } + } + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/Repeat.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/Repeat.java new file mode 100644 index 00000000000..b1a4559d8c5 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/Repeat.java @@ -0,0 +1,33 @@ +/* + * Copyright 2024 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.connection; + +/** Simple util class for repeating (test) functions. */ +class Repeat { + + /** Repeat the given {@link Runnable} twice. */ + static void twice(Runnable runnable) { + repeat(runnable, 2); + } + + /** Repeat the given {@link Runnable} n times. */ + static void repeat(Runnable runnable, int n) { + for (int ignore = 0; ignore < n; ignore++) { + runnable.run(); + } + } +} 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 6bb02a7a61f..b606d1faf58 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 @@ -40,7 +40,6 @@ import com.google.cloud.spanner.testing.RemoteSpannerHelper; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; -import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import com.google.spanner.admin.database.v1.UpdateDatabaseMetadata; import java.util.ArrayList; @@ -50,7 +49,6 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import org.junit.After; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; @@ -68,7 +66,6 @@ public class ITDatabaseAdminTest { private static final Logger logger = Logger.getLogger(ITDatabaseAdminTest.class.getName()); private DatabaseAdminClient dbAdminClient; private RemoteSpannerHelper testHelper; - private List dbs = new ArrayList<>(); @Before public void setUp() { @@ -76,25 +73,13 @@ public void setUp() { dbAdminClient = testHelper.getClient().getDatabaseAdminClient(); } - @After - public void tearDown() { - for (Database db : dbs) { - db.drop(); - } - dbs.clear(); - } - @Test public void testDatabaseOperations() throws Exception { - final String databaseId = testHelper.getUniqueDatabaseId(); final String instanceId = testHelper.getInstanceId().getInstance(); final String createTableT = "CREATE TABLE T (\n" + " K STRING(MAX),\n" + ") PRIMARY KEY(K)"; - final Database createdDatabase = - dbAdminClient - .createDatabase(instanceId, databaseId, ImmutableList.of(createTableT)) - .get(5, TimeUnit.MINUTES); - dbs.add(createdDatabase); + final Database createdDatabase = testHelper.createTestDatabase(createTableT); + final String databaseId = createdDatabase.getId().getDatabase(); assertEquals(databaseId, createdDatabase.getId().getDatabase()); assertEquals(Dialect.GOOGLE_STANDARD_SQL, createdDatabase.getDialect()); @@ -124,7 +109,6 @@ public void testDatabaseOperations() throws Exception { assertEquals(databaseDdl, ImmutableList.of(createTableT, createTableT2)); dbAdminClient.dropDatabase(instanceId, databaseId); - dbs.clear(); try { dbAdminClient.getDatabase(instanceId, databaseId); @@ -136,13 +120,11 @@ public void testDatabaseOperations() throws Exception { @Test public void updateDdlRetry() throws Exception { - String dbId = testHelper.getUniqueDatabaseId(); String instanceId = testHelper.getInstanceId().getInstance(); String statement1 = "CREATE TABLE T (\n" + " K STRING(MAX),\n" + ") PRIMARY KEY(K)"; - OperationFuture op = - dbAdminClient.createDatabase(instanceId, dbId, ImmutableList.of(statement1)); - Database db = op.get(TIMEOUT_MINUTES, TimeUnit.MINUTES); - dbs.add(db); + Database db = testHelper.createTestDatabase(statement1); + String dbId = db.getId().getDatabase(); + String statement2 = "CREATE TABLE T2 (\n" + " K2 STRING(MAX),\n" + ") PRIMARY KEY(K2)"; OperationFuture op1 = dbAdminClient.updateDatabaseDdl(instanceId, dbId, ImmutableList.of(statement2), "myop"); @@ -163,13 +145,9 @@ public void updateDdlRetry() throws Exception { @Test public void databaseOperationsViaEntity() throws Exception { - String dbId = testHelper.getUniqueDatabaseId(); - String instanceId = testHelper.getInstanceId().getInstance(); String statement1 = "CREATE TABLE T (\n" + " K STRING(MAX),\n" + ") PRIMARY KEY(K)"; - OperationFuture op = - dbAdminClient.createDatabase(instanceId, dbId, ImmutableList.of(statement1)); - Database db = op.get(TIMEOUT_MINUTES, TimeUnit.MINUTES); - dbs.add(db); + Database db = testHelper.createTestDatabase(statement1); + String dbId = db.getId().getDatabase(); assertThat(db.getId().getDatabase()).isEqualTo(dbId); db = db.reload(); @@ -181,7 +159,6 @@ public void databaseOperationsViaEntity() throws Exception { Iterable statementsInDb = db.getDdl(); assertThat(statementsInDb).containsExactly(statement1, statement2); db.drop(); - dbs.clear(); try { db.reload(); fail("Expected exception"); @@ -191,16 +168,11 @@ public void databaseOperationsViaEntity() throws Exception { } @Test - public void listPagination() throws Exception { - List dbIds = - ImmutableList.of( - testHelper.getUniqueDatabaseId(), - testHelper.getUniqueDatabaseId(), - testHelper.getUniqueDatabaseId()); - + public void listPagination() { String instanceId = testHelper.getInstanceId().getInstance(); - for (String dbId : dbIds) { - dbs.add(dbAdminClient.createDatabase(instanceId, dbId, ImmutableList.of()).get()); + List dbIds = new ArrayList<>(3); + for (int n = 0; n < 3; n++) { + dbIds.add(testHelper.createTestDatabase().getId().getDatabase()); } Page page = dbAdminClient.listDatabases(instanceId, Options.pageSize(1)); List dbIdsGot = new ArrayList<>(); @@ -228,10 +200,7 @@ public void createAndListDatabaseRoles() throws Exception { testHelper.getUniqueDatabaseRole()); String instanceId = testHelper.getInstanceId().getInstance(); - Database database = - dbAdminClient - .createDatabase(instanceId, testHelper.getUniqueDatabaseId(), ImmutableList.of()) - .get(); + Database database = testHelper.createTestDatabase(); // Create the roles in Db. List dbRolesCreateStatements = new ArrayList<>(); @@ -280,13 +249,9 @@ public void createAndListDatabaseRoles() throws Exception { } @Test - public void updateDatabaseInvalidFieldsToUpdate() throws Exception { + public void updateDatabaseInvalidFieldsToUpdate() { assumeFalse("Emulator does not drop database protection", isUsingEmulator()); - String instanceId = testHelper.getInstanceId().getInstance(); - Database database = - dbAdminClient - .createDatabase(instanceId, testHelper.getUniqueDatabaseId(), ImmutableList.of()) - .get(); + Database database = testHelper.createTestDatabase(); logger.log(Level.INFO, "Created database: {0}", database.getId().getName()); Database databaseToUpdate = @@ -304,10 +269,7 @@ public void updateDatabaseInvalidFieldsToUpdate() throws Exception { public void dropDatabaseWithProtectionEnabled() throws Exception { assumeFalse("Emulator does not drop database protection", isUsingEmulator()); String instanceId = testHelper.getInstanceId().getInstance(); - Database database = - dbAdminClient - .createDatabase(instanceId, testHelper.getUniqueDatabaseId(), ImmutableList.of()) - .get(); + Database database = testHelper.createTestDatabase(); logger.log(Level.INFO, "Created database: {0}", database.getId().getName()); // Enable drop protection for the database. 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 006d3e2c8a8..ab45eb67179 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 @@ -19,14 +19,12 @@ import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; import com.google.api.client.util.ExponentialBackOff; import com.google.api.gax.longrunning.OperationFuture; import com.google.cloud.spanner.Database; -import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.DatabaseNotFoundException; @@ -46,9 +44,6 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import javax.annotation.Nullable; import org.junit.ClassRule; import org.junit.Test; @@ -156,69 +151,54 @@ public void instanceNotFound() { } @Test - public void testNumericPrimaryKey() - throws InterruptedException, ExecutionException, TimeoutException { + public void testNumericPrimaryKey() { assumeFalse("Emulator does not support numeric primary keys", isUsingEmulator()); - final String projectId = env.getTestHelper().getInstanceId().getProject(); - final String instanceId = env.getTestHelper().getInstanceId().getInstance(); - final String databaseId = env.getTestHelper().getUniqueDatabaseId(); final String table = "NumericTable"; - final DatabaseId id = DatabaseId.of(projectId, instanceId, databaseId); - final DatabaseAdminClient databaseAdminClient = - env.getTestHelper().getClient().getDatabaseAdminClient(); - try { - // Creates table with numeric primary key - final OperationFuture operation = - databaseAdminClient.createDatabase( - instanceId, - databaseId, - Collections.singletonList( - "CREATE TABLE " + table + " (" + "Id NUMERIC NOT NULL" + ") PRIMARY KEY (Id)")); - final Database database = operation.get(10, TimeUnit.MINUTES); - assertNotNull(database); - - // Writes data into the table - final DatabaseClient databaseClient = env.getTestHelper().getClient().getDatabaseClient(id); - final ArrayList mutations = new ArrayList<>(); - for (int i = 0; i < 5; i++) { - mutations.add( - Mutation.newInsertBuilder(table).set("Id").to(new BigDecimal(i + "")).build()); - } - databaseClient.write(mutations); - - // Reads the data to verify the writes - try (final ResultSet resultSet = - databaseClient.singleUse().read(table, KeySet.all(), Collections.singletonList("Id"))) { - for (int i = 0; resultSet.next(); i++) { - assertEquals(new BigDecimal(i + ""), resultSet.getBigDecimal("Id")); - } - } + // Creates table with numeric primary key + Database database = + env.getTestHelper() + .createTestDatabase( + "CREATE TABLE " + table + " (" + "Id NUMERIC NOT NULL" + ") PRIMARY KEY (Id)"); + + // Writes data into the table + final DatabaseClient databaseClient = + env.getTestHelper().getClient().getDatabaseClient(database.getId()); + final ArrayList mutations = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + mutations.add(Mutation.newInsertBuilder(table).set("Id").to(new BigDecimal(i + "")).build()); + } + databaseClient.write(mutations); - // Deletes data from the table, leaving only the Id = 0 row - databaseClient - .readWriteTransaction() - .run( - new TransactionCallable() { - @Nullable - @Override - public Object run(TransactionContext transaction) throws Exception { - transaction.executeUpdate(Statement.of("DELETE FROM " + table + " WHERE Id > 0")); - return null; - } - }); - - // Reads the data to verify the deletes only left a single row left - try (final ResultSet resultSet = - databaseClient - .singleUse() - .executeQuery(Statement.of("SELECT COUNT(1) as cnt FROM " + table))) { - resultSet.next(); - assertEquals(1L, resultSet.getLong("cnt")); + // Reads the data to verify the writes + try (final ResultSet resultSet = + databaseClient.singleUse().read(table, KeySet.all(), Collections.singletonList("Id"))) { + for (int i = 0; resultSet.next(); i++) { + assertEquals(new BigDecimal(i + ""), resultSet.getBigDecimal("Id")); } - } finally { - databaseAdminClient.dropDatabase(instanceId, databaseId); + } + + // Deletes data from the table, leaving only the Id = 0 row + databaseClient + .readWriteTransaction() + .run( + new TransactionCallable() { + @Nullable + @Override + public Object run(TransactionContext transaction) throws Exception { + transaction.executeUpdate(Statement.of("DELETE FROM " + table + " WHERE Id > 0")); + return null; + } + }); + + // Reads the data to verify the deletes only left a single row left + try (final ResultSet resultSet = + databaseClient + .singleUse() + .executeQuery(Statement.of("SELECT COUNT(1) as cnt FROM " + table))) { + resultSet.next(); + assertEquals(1L, resultSet.getLong("cnt")); } } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITProtoColumnTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITProtoColumnTest.java index 82e51c32933..3657ef2109e 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITProtoColumnTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITProtoColumnTest.java @@ -54,12 +54,14 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.ClassRule; +import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; // Integration Tests to test DDL, DML and DQL for Proto Columns and Enums +@Ignore("Feature is not yet enabled in production") @Category(ParallelIntegrationTest.class) @RunWith(JUnit4.class) public class ITProtoColumnTest { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java index 51c47bd84cc..17f5f8e0ec9 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java @@ -304,6 +304,22 @@ public void batchWriteAtLeastOnce() { } } + @Test + public void testWriteWithMaxCommitDelay() { + CommitResponse response = + client.writeWithOptions( + Collections.singletonList( + Mutation.newInsertOrUpdateBuilder("T") + .set("K") + .to(lastKey = uniqueString()) + .set("StringValue") + .to("v1") + .build()), + Options.maxCommitDelay(java.time.Duration.ofMillis(100))); + assertNotNull(response); + assertNotNull(response.getCommitTimestamp()); + } + @Test public void testWriteReturnsCommitStats() { assumeFalse("Emulator does not return commit statistics", isUsingEmulator()); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/spi/v1/GfeLatencyTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/spi/v1/GfeLatencyTest.java index dd6006078eb..9bb09aace70 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/spi/v1/GfeLatencyTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/spi/v1/GfeLatencyTest.java @@ -17,7 +17,6 @@ package com.google.cloud.spanner.spi.v1; import static org.junit.Assert.assertEquals; -import static org.junit.Assume.assumeFalse; import com.google.auth.oauth2.AccessToken; import com.google.auth.oauth2.OAuth2Credentials; @@ -28,7 +27,6 @@ import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; -import com.google.cloud.spanner.testing.EmulatorSpannerHelper; import com.google.protobuf.ListValue; import com.google.spanner.v1.ResultSetMetadata; import com.google.spanner.v1.StructType; @@ -138,7 +136,6 @@ public class GfeLatencyTest { @BeforeClass public static void startServer() throws IOException { SpannerRpcViews.registerGfeLatencyAndHeaderMissingCountViews(); - assumeFalse(EmulatorSpannerHelper.isUsingEmulator()); mockSpanner = new MockSpannerServiceImpl(); mockSpanner.setAbortProbability(0.0D); // We don't want any unpredictable aborted transactions. diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/spi/v1/SpannerRpcMetricsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/spi/v1/SpannerRpcMetricsTest.java new file mode 100644 index 00000000000..049ce0d1960 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/spi/v1/SpannerRpcMetricsTest.java @@ -0,0 +1,299 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner.spi.v1; + +import static org.junit.Assert.assertEquals; + +import com.google.cloud.NoCredentials; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.MockSpannerServiceImpl; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.cloud.spanner.Statement; +import com.google.protobuf.ListValue; +import com.google.spanner.v1.ResultSetMetadata; +import com.google.spanner.v1.StructType; +import com.google.spanner.v1.TypeCode; +import io.grpc.ForwardingServerCall; +import io.grpc.Metadata; +import io.grpc.Server; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class SpannerRpcMetricsTest { + + private static MockSpannerServiceImpl mockSpanner; + private static Server server; + private static InetSocketAddress address; + private static Spanner spannerWithOpenTelemetry; + private static DatabaseClient databaseClient; + private static final Map optionsMap = new HashMap<>(); + private static MockSpannerServiceImpl mockSpannerNoHeader; + private static Server serverNoHeader; + private static InetSocketAddress addressNoHeader; + private static Spanner spannerNoHeaderNoOpenTelemetry; + private static DatabaseClient databaseClientNoHeader; + private static String instanceId = "fake-instance"; + private static String databaseId = "fake-database"; + private static String projectId = "fake-project"; + private static AtomicInteger fakeServerTiming = new AtomicInteger(new Random().nextInt(1000) + 1); + private static final Statement SELECT1AND2 = + Statement.of("SELECT 1 AS COL1 UNION ALL SELECT 2 AS COL1"); + private static final ResultSetMetadata SELECT1AND2_METADATA = + ResultSetMetadata.newBuilder() + .setRowType( + StructType.newBuilder() + .addFields( + StructType.Field.newBuilder() + .setName("COL1") + .setType( + com.google.spanner.v1.Type.newBuilder() + .setCode(TypeCode.INT64) + .build()) + .build()) + .build()) + .build(); + private static final com.google.spanner.v1.ResultSet SELECT1_RESULTSET = + com.google.spanner.v1.ResultSet.newBuilder() + .addRows( + ListValue.newBuilder() + .addValues(com.google.protobuf.Value.newBuilder().setStringValue("1").build()) + .build()) + .addRows( + ListValue.newBuilder() + .addValues(com.google.protobuf.Value.newBuilder().setStringValue("2").build()) + .build()) + .setMetadata(SELECT1AND2_METADATA) + .build(); + private static final Statement UPDATE_FOO_STATEMENT = + Statement.of("UPDATE FOO SET BAR=1 WHERE BAZ=2"); + + private static InMemoryMetricReader inMemoryMetricReader; + + private static InMemoryMetricReader inMemoryMetricReaderInjected; + + @BeforeClass + public static void startServer() throws IOException { + SpannerOptions.enableOpenTelemetryMetrics(); + mockSpanner = new MockSpannerServiceImpl(); + mockSpanner.setAbortProbability(0.0D); // We don't want any unpredictable aborted transactions. + mockSpanner.putStatementResult( + MockSpannerServiceImpl.StatementResult.query(SELECT1AND2, SELECT1_RESULTSET)); + mockSpanner.putStatementResult( + MockSpannerServiceImpl.StatementResult.update(UPDATE_FOO_STATEMENT, 1L)); + address = new InetSocketAddress("localhost", 0); + server = + NettyServerBuilder.forAddress(address) + .addService(mockSpanner) + .intercept( + new ServerInterceptor() { + @Override + public ServerCall.Listener interceptCall( + ServerCall serverCall, + Metadata headers, + ServerCallHandler serverCallHandler) { + return serverCallHandler.startCall( + new ForwardingServerCall.SimpleForwardingServerCall( + serverCall) { + @Override + public void sendHeaders(Metadata headers) { + headers.put( + Metadata.Key.of("server-timing", Metadata.ASCII_STRING_MARSHALLER), + String.format("gfet4t7; dur=%d", fakeServerTiming.get())); + super.sendHeaders(headers); + } + }, + headers); + } + }) + .build() + .start(); + optionsMap.put(SpannerRpc.Option.CHANNEL_HINT, 1L); + inMemoryMetricReader = InMemoryMetricReader.create(); + SdkMeterProvider sdkMeterProvider = + SdkMeterProvider.builder().registerMetricReader(inMemoryMetricReader).build(); + GlobalOpenTelemetry.resetForTest(); + OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).buildAndRegisterGlobal(); + + inMemoryMetricReaderInjected = InMemoryMetricReader.create(); + SdkMeterProvider sdkMeterProviderInjected = + SdkMeterProvider.builder().registerMetricReader(inMemoryMetricReaderInjected).build(); + + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderInjected).build(); + + spannerWithOpenTelemetry = + createSpannerOptionsWithOpenTelemetry(address, server, openTelemetry).getService(); + databaseClient = + spannerWithOpenTelemetry.getDatabaseClient( + DatabaseId.of(projectId, instanceId, databaseId)); + + mockSpannerNoHeader = new MockSpannerServiceImpl(); + mockSpannerNoHeader.setAbortProbability(0.0D); + mockSpannerNoHeader.putStatementResult( + MockSpannerServiceImpl.StatementResult.query(SELECT1AND2, SELECT1_RESULTSET)); + mockSpannerNoHeader.putStatementResult( + MockSpannerServiceImpl.StatementResult.update(UPDATE_FOO_STATEMENT, 1L)); + addressNoHeader = new InetSocketAddress("localhost", 0); + serverNoHeader = + NettyServerBuilder.forAddress(addressNoHeader) + .addService(mockSpannerNoHeader) + .build() + .start(); + spannerNoHeaderNoOpenTelemetry = + createSpannerOptions(addressNoHeader, serverNoHeader).getService(); + databaseClientNoHeader = + spannerNoHeaderNoOpenTelemetry.getDatabaseClient( + DatabaseId.of(projectId, instanceId, databaseId)); + } + + @AfterClass + public static void stopServer() throws InterruptedException { + if (spannerWithOpenTelemetry != null) { + spannerWithOpenTelemetry.close(); + server.shutdown(); + server.awaitTermination(); + } + + if (spannerNoHeaderNoOpenTelemetry != null) { + spannerNoHeaderNoOpenTelemetry.close(); + serverNoHeader.shutdown(); + serverNoHeader.awaitTermination(); + } + } + + @After + public void reset() { + mockSpanner.reset(); + mockSpannerNoHeader.reset(); + } + + @Test + public void testGfeLatencyExecuteSqlWithInjectedOpenTelemetry() throws InterruptedException { + databaseClient + .readWriteTransaction() + .run(transaction -> transaction.executeUpdate(UPDATE_FOO_STATEMENT)); + + double latency = + getGfeLatencyMetric( + getMetricData("spanner/gfe_latency", inMemoryMetricReaderInjected), + "google.spanner.v1.Spanner/ExecuteSql"); + assertEquals(fakeServerTiming.get(), latency, 0); + } + + @Test + public void testGfeMissingHeaderExecuteSqlWithGlobalOpenTelemetry() throws InterruptedException { + databaseClient + .readWriteTransaction() + .run(transaction -> transaction.executeUpdate(UPDATE_FOO_STATEMENT)); + long count = + getHeaderLatencyMetric( + getMetricData("spanner/gfe_header_missing_count", inMemoryMetricReaderInjected), + "google.spanner.v1.Spanner/ExecuteSql"); + assertEquals(0, count); + + databaseClientNoHeader + .readWriteTransaction() + .run(transaction -> transaction.executeUpdate(UPDATE_FOO_STATEMENT)); + long count1 = + getHeaderLatencyMetric( + getMetricData("spanner/gfe_header_missing_count", inMemoryMetricReader), + "google.spanner.v1.Spanner/ExecuteSql"); + assertEquals(1, count1); + } + + private static SpannerOptions createSpannerOptionsWithOpenTelemetry( + InetSocketAddress address, Server server, OpenTelemetry openTelemetry) { + + String endpoint = address.getHostString() + ":" + server.getPort(); + return SpannerOptions.newBuilder() + .setProjectId("[PROJECT]") + .setChannelConfigurator( + input -> { + input.usePlaintext(); + return input; + }) + .setHost("http://" + endpoint) + .setCredentials(NoCredentials.getInstance()) + .setOpenTelemetry(openTelemetry) + .build(); + } + + private static SpannerOptions createSpannerOptions(InetSocketAddress address, Server server) { + + String endpoint = address.getHostString() + ":" + server.getPort(); + return SpannerOptions.newBuilder() + .setProjectId("[PROJECT]") + .setChannelConfigurator( + input -> { + input.usePlaintext(); + return input; + }) + .setHost("http://" + endpoint) + .setCredentials(NoCredentials.getInstance()) + .build(); + } + + private long getHeaderLatencyMetric(MetricData metricData, String methodName) { + return metricData.getLongSumData().getPoints().stream() + .filter(x -> x.getAttributes().asMap().containsValue(methodName)) + .findFirst() + .get() + .getValue(); + } + + private double getGfeLatencyMetric(MetricData metricData, String methodName) { + return metricData.getHistogramData().getPoints().stream() + .filter(x -> x.getAttributes().asMap().containsValue(methodName)) + .findFirst() + .get() + .getMax(); + } + + private MetricData getMetricData(String metricName, InMemoryMetricReader inMemoryMetricReader) { + Collection metricDataCollection = inMemoryMetricReader.collectAllMetrics(); + Collection metricDataFiltered = + metricDataCollection.stream() + .filter(x -> x.getName().equals(metricName)) + .collect(Collectors.toList()); + return metricDataFiltered.stream().findFirst().get(); + } +} diff --git a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ClientSideStatementsTest.sql b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ClientSideStatementsTest.sql index 0c15e5462da..878408a5fa1 100644 --- a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ClientSideStatementsTest.sql +++ b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ClientSideStatementsTest.sql @@ -1931,6 +1931,205 @@ NEW_CONNECTION; @EXPECT EXCEPTION UNIMPLEMENTED show variable/-read_only_staleness; NEW_CONNECTION; +show variable directed_read; +NEW_CONNECTION; +SHOW VARIABLE DIRECTED_READ; +NEW_CONNECTION; +show variable directed_read; +NEW_CONNECTION; + show variable directed_read; +NEW_CONNECTION; + show variable directed_read; +NEW_CONNECTION; + + + +show variable directed_read; +NEW_CONNECTION; +show variable directed_read ; +NEW_CONNECTION; +show variable directed_read ; +NEW_CONNECTION; +show variable directed_read + +; +NEW_CONNECTION; +show variable directed_read; +NEW_CONNECTION; +show variable directed_read; +NEW_CONNECTION; +show +variable +directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +foo show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read bar; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +%show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read%; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable%directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +_show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read_; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable_directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +&show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read&; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable&directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +$show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read$; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable$directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +@show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read@; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable@directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +!show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read!; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable!directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +*show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read*; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable*directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +(show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read(; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable(directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +)show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read); +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable)directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read-; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable-directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT ++show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read+; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable+directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-#show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read-#; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable-#directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read/; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable/directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +\show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read\; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable\directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +?show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read?; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable?directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-/show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read-/; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable-/directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/#show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read/#; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable/#directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/-show variable directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable directed_read/-; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable/-directed_read; +NEW_CONNECTION; show variable optimizer_version; NEW_CONNECTION; SHOW VARIABLE OPTIMIZER_VERSION; @@ -13196,6 +13395,394 @@ NEW_CONNECTION; @EXPECT EXCEPTION INVALID_ARGUMENT set read_only_staleness='EXACT_STALENESS/-9999ns'; NEW_CONNECTION; +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; + set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; + set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; + + + +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}' ; +NEW_CONNECTION; +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}' ; +NEW_CONNECTION; +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}' + +; +NEW_CONNECTION; +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +set +directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +foo set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}' bar; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +%set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'%; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set%directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +_set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'_; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set_directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +&set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'&; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set&directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +$set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'$; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set$directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +@set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'@; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set@directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +!set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'!; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set!directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +*set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'*; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set*directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +(set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'(; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set(directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +)set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'); +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set)directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'-; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT ++set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'+; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set+directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-#set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'-#; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-#directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'/; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +\set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'\; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set\directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +?set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'?; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set?directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-/set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'-/; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-/directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/#set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'/#; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/#directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/-set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'/-; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/-directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +set directed_read=''; +NEW_CONNECTION; + set directed_read=''; +NEW_CONNECTION; + set directed_read=''; +NEW_CONNECTION; + + + +set directed_read=''; +NEW_CONNECTION; +set directed_read='' ; +NEW_CONNECTION; +set directed_read='' ; +NEW_CONNECTION; +set directed_read='' + +; +NEW_CONNECTION; +set directed_read=''; +NEW_CONNECTION; +set directed_read=''; +NEW_CONNECTION; +set +directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +foo set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read='' bar; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +%set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''%; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set%directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +_set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''_; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set_directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +&set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''&; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set&directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +$set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''$; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set$directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +@set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''@; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set@directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +!set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''!; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set!directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +*set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''*; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set*directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +(set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''(; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set(directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +)set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''); +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set)directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''-; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT ++set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''+; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set+directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-#set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''-#; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-#directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''/; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +\set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''\; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set\directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +?set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''?; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set?directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-/set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''-/; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-/directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/#set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''/#; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/#directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/-set directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set directed_read=''/-; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/-directed_read=''; +NEW_CONNECTION; set optimizer_version='1'; NEW_CONNECTION; SET OPTIMIZER_VERSION='1'; diff --git a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ConnectionImplGeneratedSqlScriptTest.sql b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ConnectionImplGeneratedSqlScriptTest.sql index 03102fd6073..087b2a9acad 100644 --- a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ConnectionImplGeneratedSqlScriptTest.sql +++ b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ConnectionImplGeneratedSqlScriptTest.sql @@ -162,15 +162,15 @@ NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=TRUE; SET READ_ONLY_STALENESS='MAX_STALENESS 10s'; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:47.919000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:47.919000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:19.121000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:19.121000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=TRUE; SET READ_ONLY_STALENESS='MAX_STALENESS 10s'; -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:47.919000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:47.919000000Z' +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:19.121000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:19.121000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; @@ -577,8 +577,8 @@ SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; SELECT 1 AS TEST; COMMIT; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:48.217000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:48.217000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:19.478000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:19.478000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; @@ -586,8 +586,8 @@ SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; SELECT 1 AS TEST; COMMIT; -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:48.217000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:48.217000000Z' +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:19.478000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:19.478000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; @@ -970,15 +970,15 @@ NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:48.474000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:48.474000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:19.808000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:19.808000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:48.475000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:19.808000000Z'; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=TRUE; @@ -1316,15 +1316,15 @@ NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=TRUE; SELECT 1 AS TEST; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:48.698000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:48.698000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:20.055000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:20.055000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=TRUE; SELECT 1 AS TEST; -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:48.698000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:48.698000000Z' +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:20.055000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:20.055000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; @@ -1671,15 +1671,15 @@ NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=TRUE; SELECT 1 AS TEST; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:48.991000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:48.991000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:20.470000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:20.470000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=TRUE; SELECT 1 AS TEST; -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:48.991000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:48.991000000Z' +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:20.470000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:20.470000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; @@ -1996,14 +1996,14 @@ SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=TRUE; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:49.241000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:49.241000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:20.734000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:20.734000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=TRUE; -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:49.241000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:49.241000000Z' +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:20.734000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:20.734000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; @@ -2325,15 +2325,15 @@ NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; COMMIT; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:49.397000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:49.397000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:20.932000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:20.932000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; COMMIT; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:49.397000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:20.932000000Z'; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -2675,15 +2675,15 @@ NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; SET READ_ONLY_STALENESS='EXACT_STALENESS 10s'; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:49.565000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:49.565000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:21.099000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:21.099000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; SET READ_ONLY_STALENESS='EXACT_STALENESS 10s'; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:49.565000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:21.099000000Z'; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -3115,8 +3115,8 @@ BEGIN TRANSACTION; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; ROLLBACK; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:49.735000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:49.735000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:21.259000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:21.259000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; @@ -3126,7 +3126,7 @@ BEGIN TRANSACTION; SELECT 1 AS TEST; ROLLBACK; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:49.735000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:21.259000000Z'; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -3627,8 +3627,8 @@ BEGIN TRANSACTION; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; COMMIT; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:49.919000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:49.919000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:21.436000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:21.436000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; @@ -3638,7 +3638,7 @@ BEGIN TRANSACTION; SELECT 1 AS TEST; COMMIT; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:49.919000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:21.436000000Z'; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -4041,15 +4041,15 @@ NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; BEGIN TRANSACTION; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.090000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:50.090000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:21.587000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:21.587000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; BEGIN TRANSACTION; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.090000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:21.587000000Z'; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -4408,14 +4408,14 @@ SET AUTOCOMMIT=FALSE; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.216000000Z'; +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:21.769000000Z'; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.216000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:21.769000000Z'; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -4765,13 +4765,13 @@ SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.371000000Z'; +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:21.960000000Z'; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.371000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:21.960000000Z'; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -5075,14 +5075,14 @@ SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.519000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:50.519000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:22.101000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:22.101000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.519000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:22.101000000Z'; NEW_CONNECTION; SET READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -5394,13 +5394,13 @@ SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; START BATCH DDL; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.625000000Z'; +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:22.206000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; START BATCH DDL; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.625000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:22.206000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; @@ -5755,8 +5755,8 @@ SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; SET TRANSACTION READ ONLY; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.719000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:50.719000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:22.306000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:22.306000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; @@ -5764,7 +5764,7 @@ SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; SET TRANSACTION READ ONLY; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.719000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:22.306000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; @@ -6199,8 +6199,8 @@ SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; UPDATE foo SET bar=1; COMMIT; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.893000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:50.893000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:22.480000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:22.480000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; @@ -6208,8 +6208,8 @@ SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; UPDATE foo SET bar=1; COMMIT; -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.893000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:50.893000000Z' +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:22.480000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:22.480000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; @@ -6595,15 +6595,15 @@ NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:51.087000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:51.087000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:22.662000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:22.662000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:51.087000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:22.662000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; @@ -6954,15 +6954,15 @@ NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id); -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:51.238000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:51.238000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:22.808000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:22.808000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id); -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:51.238000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:51.238000000Z' +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:22.808000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:22.808000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; @@ -7322,15 +7322,15 @@ NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; UPDATE foo SET bar=1; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:51.430000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:51.430000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:22.962000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:22.962000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; UPDATE foo SET bar=1; -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:51.430000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:51.430000000Z' +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:22.962000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:22.962000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; @@ -7720,16 +7720,16 @@ SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:51.584000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:51.584000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:23.132000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:23.132000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:51.584000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:51.584000000Z' +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:23.132000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:23.132000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; @@ -8112,15 +8112,15 @@ NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; SELECT 1 AS TEST; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:51.737000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:51.737000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:23.314000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:23.314000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; SELECT 1 AS TEST; -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:51.737000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:51.737000000Z' +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:23.314000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:23.314000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; @@ -8450,14 +8450,14 @@ SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:51.883000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:51.883000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:23.477000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:23.477000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=TRUE; -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:51.883000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:51.883000000Z' +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:23.477000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:23.477000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; @@ -8788,15 +8788,15 @@ NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; COMMIT; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:52.014000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:52.014000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:23.602000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:23.602000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; COMMIT; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:52.014000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:23.602000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -9206,8 +9206,8 @@ SET AUTOCOMMIT=FALSE; START BATCH DDL; CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id); RUN BATCH; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:52.137000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:52.137000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:23.741000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:23.741000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; @@ -9216,7 +9216,7 @@ START BATCH DDL; CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id); RUN BATCH; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:52.137000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:23.741000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -9625,14 +9625,14 @@ SET AUTOCOMMIT=FALSE; START BATCH DDL; CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id); @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:52.325000000Z'; +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:23.906000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; START BATCH DDL; CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id); @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:52.325000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:23.906000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -9982,13 +9982,13 @@ SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; START BATCH DDL; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:52.433000000Z'; +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.025000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; START BATCH DDL; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:52.433000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.025000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -10422,8 +10422,8 @@ SET TRANSACTION READ ONLY; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; COMMIT; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:52.570000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:52.570000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.143000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:24.143000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; @@ -10433,7 +10433,7 @@ SET TRANSACTION READ ONLY; SELECT 1 AS TEST; COMMIT; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:52.570000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.143000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -10833,15 +10833,15 @@ NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; SET TRANSACTION READ ONLY; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:52.741000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:52.741000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.282000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:24.282000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; SET TRANSACTION READ ONLY; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:52.741000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.282000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -11187,15 +11187,15 @@ NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; SET READ_ONLY_STALENESS='EXACT_STALENESS 10s'; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:52.859000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:52.859000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.383000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:24.383000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; SET READ_ONLY_STALENESS='EXACT_STALENESS 10s'; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:52.859000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.383000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -11635,8 +11635,8 @@ BEGIN TRANSACTION; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; ROLLBACK; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:53.021000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:53.021000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.512000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:24.512000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; @@ -11646,7 +11646,7 @@ BEGIN TRANSACTION; SELECT 1 AS TEST; ROLLBACK; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:53.021000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.512000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -12155,8 +12155,8 @@ BEGIN TRANSACTION; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; COMMIT; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:53.401000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:53.401000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.703000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:24.703000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; @@ -12166,7 +12166,7 @@ BEGIN TRANSACTION; SELECT 1 AS TEST; COMMIT; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:53.401000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.703000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -12571,15 +12571,15 @@ NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; BEGIN TRANSACTION; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:53.677000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:53.677000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.875000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:24.875000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; BEGIN TRANSACTION; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:53.677000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.875000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -12942,14 +12942,14 @@ SET AUTOCOMMIT=FALSE; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:54.023000000Z'; +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.992000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:54.024000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.992000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -13304,13 +13304,13 @@ SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:54.361000000Z'; +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:25.198000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:54.361000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:25.198000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -13624,14 +13624,14 @@ SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; -SET READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:54.579000000Z'; -@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:54.579000000Z' +SET READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:25.346000000Z'; +@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:25.346000000Z' SHOW VARIABLE READ_ONLY_STALENESS; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; @EXPECT EXCEPTION FAILED_PRECONDITION -SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:54.579000000Z'; +SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:25.346000000Z'; NEW_CONNECTION; SET READONLY=FALSE; SET AUTOCOMMIT=FALSE; diff --git a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/postgresql/ClientSideStatementsTest.sql b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/postgresql/ClientSideStatementsTest.sql index 9c66ea40b18..e8843ab6aa7 100644 --- a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/postgresql/ClientSideStatementsTest.sql +++ b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/postgresql/ClientSideStatementsTest.sql @@ -3855,6 +3855,403 @@ NEW_CONNECTION; @EXPECT EXCEPTION UNIMPLEMENTED show variable/-spanner.read_only_staleness; NEW_CONNECTION; +show spanner.directed_read; +NEW_CONNECTION; +SHOW SPANNER.DIRECTED_READ; +NEW_CONNECTION; +show spanner.directed_read; +NEW_CONNECTION; + show spanner.directed_read; +NEW_CONNECTION; + show spanner.directed_read; +NEW_CONNECTION; + + + +show spanner.directed_read; +NEW_CONNECTION; +show spanner.directed_read ; +NEW_CONNECTION; +show spanner.directed_read ; +NEW_CONNECTION; +show spanner.directed_read + +; +NEW_CONNECTION; +show spanner.directed_read; +NEW_CONNECTION; +show spanner.directed_read; +NEW_CONNECTION; +show +spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +foo show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read bar; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +%show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read%; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show%spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +_show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read_; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show_spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +&show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read&; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show&spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +$show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read$; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show$spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +@show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read@; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show@spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +!show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read!; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show!spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +*show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read*; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show*spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +(show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read(; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show(spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +)show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read); +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show)spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read-; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show-spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT ++show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read+; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show+spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-#show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read-#; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show-#spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read/; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show/spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +\show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read\; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show\spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +?show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read?; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show?spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-/show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read-/; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show-/spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/#show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read/#; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show/#spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/-show spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show spanner.directed_read/-; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +show/-spanner.directed_read; +NEW_CONNECTION; +show variable spanner.directed_read; +NEW_CONNECTION; +SHOW VARIABLE SPANNER.DIRECTED_READ; +NEW_CONNECTION; +show variable spanner.directed_read; +NEW_CONNECTION; + show variable spanner.directed_read; +NEW_CONNECTION; + show variable spanner.directed_read; +NEW_CONNECTION; + + + +show variable spanner.directed_read; +NEW_CONNECTION; +show variable spanner.directed_read ; +NEW_CONNECTION; +show variable spanner.directed_read ; +NEW_CONNECTION; +show variable spanner.directed_read + +; +NEW_CONNECTION; +show variable spanner.directed_read; +NEW_CONNECTION; +show variable spanner.directed_read; +NEW_CONNECTION; +show +variable +spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +foo show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read bar; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +%show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read%; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable%spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +_show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read_; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable_spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +&show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read&; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable&spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +$show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read$; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable$spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +@show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read@; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable@spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +!show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read!; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable!spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +*show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read*; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable*spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +(show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read(; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable(spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +)show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read); +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable)spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read-; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable-spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT ++show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read+; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable+spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-#show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read-#; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable-#spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read/; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable/spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +\show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read\; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable\spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +?show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read?; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable?spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-/show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read-/; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable-/spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/#show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read/#; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable/#spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/-show variable spanner.directed_read; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable spanner.directed_read/-; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable/-spanner.directed_read; +NEW_CONNECTION; show spanner.optimizer_version; NEW_CONNECTION; SHOW SPANNER.OPTIMIZER_VERSION; @@ -62155,6 +62552,394 @@ NEW_CONNECTION; @EXPECT EXCEPTION INVALID_ARGUMENT set spanner.read_only_staleness to 'EXACT_STALENESS/-9999ns'; NEW_CONNECTION; +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; + set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; + set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; + + + +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}' ; +NEW_CONNECTION; +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}' ; +NEW_CONNECTION; +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}' + +; +NEW_CONNECTION; +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +set +spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +foo set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}' bar; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +%set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'%; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set%spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +_set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'_; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set_spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +&set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'&; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set&spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +$set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'$; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set$spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +@set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'@; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set@spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +!set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'!; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set!spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +*set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'*; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set*spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +(set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'(; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set(spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +)set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'); +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set)spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'-; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT ++set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'+; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set+spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-#set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'-#; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-#spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'/; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +\set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'\; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set\spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +?set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'?; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set?spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-/set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'-/; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-/spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/#set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'/#; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/#spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/-set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'/-; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/-spanner.directed_read='{"includeReplicas":{"replicaSelections":[{"location":"eu-west1","type":"READ_ONLY"}]}}'; +NEW_CONNECTION; +set spanner.directed_read=''; +NEW_CONNECTION; + set spanner.directed_read=''; +NEW_CONNECTION; + set spanner.directed_read=''; +NEW_CONNECTION; + + + +set spanner.directed_read=''; +NEW_CONNECTION; +set spanner.directed_read='' ; +NEW_CONNECTION; +set spanner.directed_read='' ; +NEW_CONNECTION; +set spanner.directed_read='' + +; +NEW_CONNECTION; +set spanner.directed_read=''; +NEW_CONNECTION; +set spanner.directed_read=''; +NEW_CONNECTION; +set +spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +foo set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read='' bar; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +%set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''%; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set%spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +_set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''_; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set_spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +&set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''&; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set&spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +$set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''$; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set$spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +@set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''@; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set@spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +!set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''!; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set!spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +*set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''*; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set*spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +(set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''(; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set(spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +)set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''); +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set)spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''-; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT ++set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''+; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set+spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-#set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''-#; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-#spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''/; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +\set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''\; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set\spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +?set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''?; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set?spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-/set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''-/; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-/spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/#set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''/#; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/#spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/-set spanner.directed_read=''; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set spanner.directed_read=''/-; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/-spanner.directed_read=''; +NEW_CONNECTION; set spanner.optimizer_version='1'; NEW_CONNECTION; SET SPANNER.OPTIMIZER_VERSION='1'; diff --git a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/postgresql/ConnectionImplGeneratedSqlScriptTest.sql b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/postgresql/ConnectionImplGeneratedSqlScriptTest.sql index f1e2847b2f7..32f07949e9d 100644 --- a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/postgresql/ConnectionImplGeneratedSqlScriptTest.sql +++ b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/postgresql/ConnectionImplGeneratedSqlScriptTest.sql @@ -162,15 +162,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=TRUE; SET SPANNER.READ_ONLY_STALENESS='MAX_STALENESS 10s'; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:48.073000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:48.073000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:19.303000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:19.303000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=TRUE; SET SPANNER.READ_ONLY_STALENESS='MAX_STALENESS 10s'; -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:48.073000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:48.073000000Z' +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:19.303000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:19.303000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; @@ -577,8 +577,8 @@ SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; SELECT 1 AS TEST; COMMIT; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:48.359000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:48.359000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:19.659000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:19.659000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; @@ -586,8 +586,8 @@ SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; SELECT 1 AS TEST; COMMIT; -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:48.359000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:48.359000000Z' +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:19.659000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:19.659000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; @@ -970,15 +970,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:48.581000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:48.581000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:19.923000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:19.923000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:48.581000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:19.923000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=TRUE; @@ -1316,15 +1316,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=TRUE; SELECT 1 AS TEST; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:48.846000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:48.846000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:20.233000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:20.233000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=TRUE; SELECT 1 AS TEST; -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:48.846000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:48.846000000Z' +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:20.233000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:20.233000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; @@ -1671,15 +1671,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=TRUE; SELECT 1 AS TEST; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:49.135000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:49.135000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:20.600000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:20.600000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=TRUE; SELECT 1 AS TEST; -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:49.135000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:49.135000000Z' +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:20.600000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:20.600000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; @@ -1996,14 +1996,14 @@ SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=TRUE; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:49.319000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:49.319000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:20.834000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:20.834000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=TRUE; -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:49.319000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:49.319000000Z' +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:20.834000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:20.834000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; @@ -2325,15 +2325,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; COMMIT; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:49.485000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:49.485000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:21.015000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:21.015000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; COMMIT; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:49.485000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:21.015000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -2675,15 +2675,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; SET SPANNER.READ_ONLY_STALENESS='EXACT_STALENESS 10s'; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:49.643000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:49.643000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:21.175000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:21.175000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; SET SPANNER.READ_ONLY_STALENESS='EXACT_STALENESS 10s'; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:49.643000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:21.175000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -3115,8 +3115,8 @@ BEGIN TRANSACTION; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; ROLLBACK; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:49.827000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:49.827000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:21.351000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:21.351000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; @@ -3126,7 +3126,7 @@ BEGIN TRANSACTION; SELECT 1 AS TEST; ROLLBACK; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:49.827000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:21.351000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -3627,8 +3627,8 @@ BEGIN TRANSACTION; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; COMMIT; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.013000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:50.013000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:21.518000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:21.518000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; @@ -3638,7 +3638,7 @@ BEGIN TRANSACTION; SELECT 1 AS TEST; COMMIT; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.013000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:21.518000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -4041,15 +4041,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; BEGIN TRANSACTION; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.147000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:50.147000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:21.677000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:21.677000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; BEGIN TRANSACTION; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.147000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:21.677000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -4408,14 +4408,14 @@ SET AUTOCOMMIT=FALSE; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.291000000Z'; +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:21.872000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.291000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:21.872000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -4765,13 +4765,13 @@ SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.445000000Z'; +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:22.037000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.445000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:22.037000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -5075,14 +5075,14 @@ SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.572000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:50.572000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:22.154000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:22.154000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.572000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:22.154000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=TRUE; SET AUTOCOMMIT=FALSE; @@ -5394,13 +5394,13 @@ SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; START BATCH DDL; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.673000000Z'; +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:22.255000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; START BATCH DDL; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.673000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:22.255000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; @@ -5755,8 +5755,8 @@ SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; SET TRANSACTION READ ONLY; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.763000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:50.763000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:22.355000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:22.355000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; @@ -5764,7 +5764,7 @@ SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; SET TRANSACTION READ ONLY; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.763000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:22.355000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; @@ -6199,8 +6199,8 @@ SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; UPDATE foo SET bar=1; COMMIT; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:50.993000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:50.993000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:22.572000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:22.572000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; @@ -6208,8 +6208,8 @@ SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; UPDATE foo SET bar=1; COMMIT; -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:50.993000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:50.993000000Z' +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:22.572000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:22.572000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; @@ -6595,15 +6595,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:51.171000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:51.171000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:22.724000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:22.724000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; BEGIN TRANSACTION; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:51.171000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:22.724000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; @@ -6954,15 +6954,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id); -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:51.339000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:51.339000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:22.886000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:22.886000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id); -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:51.339000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:51.339000000Z' +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:22.886000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:22.886000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; @@ -7322,15 +7322,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; UPDATE foo SET bar=1; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:51.508000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:51.508000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:23.046000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:23.046000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; UPDATE foo SET bar=1; -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:51.508000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:51.508000000Z' +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:23.046000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:23.046000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; @@ -7720,16 +7720,16 @@ SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:51.661000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:51.661000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:23.227000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:23.227000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:51.661000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:51.661000000Z' +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:23.227000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:23.227000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; @@ -8112,15 +8112,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; SELECT 1 AS TEST; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:51.816000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:51.816000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:23.402000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:23.402000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; SELECT 1 AS TEST; -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:51.816000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:51.816000000Z' +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:23.402000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:23.402000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; @@ -8450,14 +8450,14 @@ SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:51.956000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:51.956000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:23.535000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:23.535000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=TRUE; -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:51.956000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2023-11-21T11:20:51.956000000Z' +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:23.535000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2024-01-31T15:41:23.535000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; @@ -8788,15 +8788,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; COMMIT; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:52.071000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:52.071000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:23.671000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:23.671000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; COMMIT; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:52.071000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:23.671000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -9206,8 +9206,8 @@ SET AUTOCOMMIT=FALSE; START BATCH DDL; CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id); RUN BATCH; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:52.257000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:52.257000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:23.829000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:23.829000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; @@ -9216,7 +9216,7 @@ START BATCH DDL; CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id); RUN BATCH; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:52.257000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:23.829000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -9625,14 +9625,14 @@ SET AUTOCOMMIT=FALSE; START BATCH DDL; CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id); @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:52.381000000Z'; +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:23.965000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; START BATCH DDL; CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id); @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:52.381000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:23.965000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -9982,13 +9982,13 @@ SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; START BATCH DDL; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:52.480000000Z'; +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.077000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; START BATCH DDL; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:52.480000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.077000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -10422,8 +10422,8 @@ SET TRANSACTION READ ONLY; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; COMMIT; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:52.666000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:52.666000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.219000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:24.219000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; @@ -10433,7 +10433,7 @@ SET TRANSACTION READ ONLY; SELECT 1 AS TEST; COMMIT; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:52.666000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.219000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -10833,15 +10833,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; SET TRANSACTION READ ONLY; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:52.788000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:52.788000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.331000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:24.331000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; SET TRANSACTION READ ONLY; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:52.788000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.331000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -11187,15 +11187,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; SET SPANNER.READ_ONLY_STALENESS='EXACT_STALENESS 10s'; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:52.927000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:52.927000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.438000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:24.438000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; SET SPANNER.READ_ONLY_STALENESS='EXACT_STALENESS 10s'; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:52.927000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.438000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -11635,8 +11635,8 @@ BEGIN TRANSACTION; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; ROLLBACK; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:53.139000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:53.139000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.605000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:24.605000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; @@ -11646,7 +11646,7 @@ BEGIN TRANSACTION; SELECT 1 AS TEST; ROLLBACK; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:53.139000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.605000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -12155,8 +12155,8 @@ BEGIN TRANSACTION; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; COMMIT; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:53.540000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:53.540000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.798000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:24.798000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; @@ -12166,7 +12166,7 @@ BEGIN TRANSACTION; SELECT 1 AS TEST; COMMIT; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:53.540000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.798000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -12571,15 +12571,15 @@ NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; BEGIN TRANSACTION; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:53.794000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:53.794000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:24.928000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:24.928000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; BEGIN TRANSACTION; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:53.794000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:24.928000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -12942,14 +12942,14 @@ SET AUTOCOMMIT=FALSE; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:54.142000000Z'; +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:25.098000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; @EXPECT RESULT_SET 'TEST',1 SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:54.142000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:25.098000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -13304,13 +13304,13 @@ SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:54.485000000Z'; +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:25.279000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; SELECT 1 AS TEST; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:54.485000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:25.279000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; @@ -13624,14 +13624,14 @@ SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; -SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2023-11-21T11:20:54.634000000Z'; -@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2023-11-21T11:20:54.634000000Z' +SET SPANNER.READ_ONLY_STALENESS='READ_TIMESTAMP 2024-01-31T15:41:25.401000000Z'; +@EXPECT RESULT_SET 'SPANNER.READ_ONLY_STALENESS','READ_TIMESTAMP 2024-01-31T15:41:25.401000000Z' SHOW VARIABLE SPANNER.READ_ONLY_STALENESS; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; @EXPECT EXCEPTION FAILED_PRECONDITION -SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2023-11-21T11:20:54.634000000Z'; +SET SPANNER.READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2024-01-31T15:41:25.401000000Z'; NEW_CONNECTION; SET SPANNER.READONLY=FALSE; SET AUTOCOMMIT=FALSE; diff --git a/grpc-google-cloud-spanner-admin-database-v1/pom.xml b/grpc-google-cloud-spanner-admin-database-v1/pom.xml index efcd9b7acba..be7ce3b4dc7 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 - 6.57.0 + 6.58.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 - 6.57.0 + 6.58.0 diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml index f52e0fc618d..b3645ac4f41 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 - 6.57.0 + 6.58.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 - 6.57.0 + 6.58.0 diff --git a/grpc-google-cloud-spanner-executor-v1/pom.xml b/grpc-google-cloud-spanner-executor-v1/pom.xml index c77e7989388..1f9c0bb75c8 100644 --- a/grpc-google-cloud-spanner-executor-v1/pom.xml +++ b/grpc-google-cloud-spanner-executor-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-executor-v1 - 6.57.0 + 6.58.0 grpc-google-cloud-spanner-executor-v1 GRPC library for google-cloud-spanner com.google.cloud google-cloud-spanner-parent - 6.57.0 + 6.58.0 diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml index 7a99b0b5a61..e41d7181438 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 - 6.57.0 + 6.58.0 grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.57.0 + 6.58.0 diff --git a/owlbot.py b/owlbot.py index 572889afb4e..4b41b1cf272 100644 --- a/owlbot.py +++ b/owlbot.py @@ -46,5 +46,7 @@ ".kokoro/common.sh", ".kokoro/build.sh", ".kokoro/dependencies.sh", + ".kokoro/requirements.in", + ".kokoro/requirements.txt" ] ) diff --git a/pom.xml b/pom.xml index 38cd574b29e..9eaf48ff710 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-spanner-parent pom - 6.57.0 + 6.58.0 Google Cloud Spanner Parent https://github.com/googleapis/java-spanner @@ -54,7 +54,7 @@ UTF-8 github google-cloud-spanner-parent - 3.23.0 + 3.24.0 @@ -62,47 +62,55 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.57.0 + 6.58.0 com.google.api.grpc proto-google-cloud-spanner-executor-v1 - 6.57.0 + 6.58.0 com.google.api.grpc grpc-google-cloud-spanner-executor-v1 - 6.57.0 + 6.58.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.57.0 + 6.58.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.57.0 + 6.58.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.57.0 + 6.58.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.57.0 + 6.58.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.57.0 + 6.58.0 com.google.cloud google-cloud-spanner - 6.57.0 + 6.58.0 + + + + io.opentelemetry + opentelemetry-bom + 1.34.1 + 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 ba2ef61db7b..afc8a9d036c 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 - 6.57.0 + 6.58.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 - 6.57.0 + 6.58.0 diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml index 142ed3179ab..30c4451663c 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 - 6.57.0 + 6.58.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 - 6.57.0 + 6.58.0 diff --git a/proto-google-cloud-spanner-executor-v1/pom.xml b/proto-google-cloud-spanner-executor-v1/pom.xml index ba0ff626523..4890cc004b3 100644 --- a/proto-google-cloud-spanner-executor-v1/pom.xml +++ b/proto-google-cloud-spanner-executor-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-executor-v1 - 6.57.0 + 6.58.0 proto-google-cloud-spanner-executor-v1 Proto library for google-cloud-spanner com.google.cloud google-cloud-spanner-parent - 6.57.0 + 6.58.0 diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml index 922262b8344..0691dc941f4 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 - 6.57.0 + 6.58.0 proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.57.0 + 6.58.0 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index f518465160d..a17883b0acc 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -32,7 +32,7 @@ com.google.cloud google-cloud-spanner - 6.57.0 + 6.58.0 diff --git a/samples/snippets/src/main/java/com/example/spanner/TracingSample.java b/samples/snippets/src/main/java/com/example/spanner/TracingSample.java index 9dcd90d7705..a3d4e5bd072 100644 --- a/samples/snippets/src/main/java/com/example/spanner/TracingSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/TracingSample.java @@ -32,7 +32,11 @@ import io.opencensus.trace.samplers.Samplers; import java.util.Arrays; -/** This sample demonstrates how to enable opencensus tracing and stats in cloud spanner client. */ +/** This sample demonstrates how to enable opencensus tracing and stats in cloud spanner client. + * + * @deprecated The OpenCensus project is deprecated. Use OpenTelemetry to enable metrics + * and stats with cloud spanner client. +*/ public class TracingSample { private static final String SAMPLE_SPAN = "CloudSpannerSample"; diff --git a/versions.txt b/versions.txt index 5543d65ee61..5bfa2637503 100644 --- a/versions.txt +++ b/versions.txt @@ -1,13 +1,13 @@ # Format: # module:released-version:current-version -proto-google-cloud-spanner-admin-instance-v1:6.57.0:6.57.0 -proto-google-cloud-spanner-v1:6.57.0:6.57.0 -proto-google-cloud-spanner-admin-database-v1:6.57.0:6.57.0 -grpc-google-cloud-spanner-v1:6.57.0:6.57.0 -grpc-google-cloud-spanner-admin-instance-v1:6.57.0:6.57.0 -grpc-google-cloud-spanner-admin-database-v1:6.57.0:6.57.0 -google-cloud-spanner:6.57.0:6.57.0 -google-cloud-spanner-executor:6.57.0:6.57.0 -proto-google-cloud-spanner-executor-v1:6.57.0:6.57.0 -grpc-google-cloud-spanner-executor-v1:6.57.0:6.57.0 +proto-google-cloud-spanner-admin-instance-v1:6.58.0:6.58.0 +proto-google-cloud-spanner-v1:6.58.0:6.58.0 +proto-google-cloud-spanner-admin-database-v1:6.58.0:6.58.0 +grpc-google-cloud-spanner-v1:6.58.0:6.58.0 +grpc-google-cloud-spanner-admin-instance-v1:6.58.0:6.58.0 +grpc-google-cloud-spanner-admin-database-v1:6.58.0:6.58.0 +google-cloud-spanner:6.58.0:6.58.0 +google-cloud-spanner-executor:6.58.0:6.58.0 +proto-google-cloud-spanner-executor-v1:6.58.0:6.58.0 +grpc-google-cloud-spanner-executor-v1:6.58.0:6.58.0