diff --git a/.github/generated-files-bot.yml b/.github/generated-files-bot.yml
index c644a24e112..e58cdcbad65 100644
--- a/.github/generated-files-bot.yml
+++ b/.github/generated-files-bot.yml
@@ -6,6 +6,7 @@ externalManifests:
file: '.github/readme/synth.metadata/synth.metadata'
jsonpath: '$.generatedFiles[*]'
ignoreAuthors:
+- 'cloud-java-bot'
- 'renovate-bot'
- 'yoshi-automation'
- 'release-please[bot]'
diff --git a/.github/scripts/update_generation_config.sh b/.github/scripts/update_generation_config.sh
index 91434688cc5..fff56bf5ddf 100644
--- a/.github/scripts/update_generation_config.sh
+++ b/.github/scripts/update_generation_config.sh
@@ -15,8 +15,15 @@ set -e
function get_latest_released_version() {
local group_id=$1
local artifact_id=$2
- latest=$(curl -s "https://search.maven.org/solrsearch/select?q=g:${group_id}+AND+a:${artifact_id}&core=gav&rows=500&wt=json" | jq -r '.response.docs[] | select(.v | test("^[0-9]+(\\.[0-9]+)*$")) | .v' | sort -V | tail -n 1)
- echo "${latest}"
+ json_content=$(curl -s "https://search.maven.org/solrsearch/select?q=g:${group_id}+AND+a:${artifact_id}&core=gav&rows=500&wt=json")
+ latest=$(jq -r '.response.docs[] | select(.v | test("^[0-9]+(\\.[0-9]+)*$")) | .v' <<< "${json_content}" | sort -V | tail -n 1)
+ if [[ -z "${latest}" ]]; then
+ echo "The latest version of ${group_id}:${artifact_id} is empty."
+ echo "The returned json from maven.org is invalid: ${json_content}"
+ exit 1
+ else
+ echo "${latest}"
+ fi
}
# Update a key to a new value in the generation config.
diff --git a/.github/trusted-contribution.yml b/.github/trusted-contribution.yml
index a0ba1f7d907..88d3ac9bf1a 100644
--- a/.github/trusted-contribution.yml
+++ b/.github/trusted-contribution.yml
@@ -1,3 +1,9 @@
trustedContributors:
- renovate-bot
- gcf-owl-bot[bot]
+
+annotations:
+- type: comment
+ text: "/gcbrun"
+- type: label
+ text: "kokoro:force-run"
diff --git a/.github/workflows/hermetic_library_generation.yaml b/.github/workflows/hermetic_library_generation.yaml
index ca1baea12a6..6b856abdef3 100644
--- a/.github/workflows/hermetic_library_generation.yaml
+++ b/.github/workflows/hermetic_library_generation.yaml
@@ -37,7 +37,7 @@ jobs:
with:
fetch-depth: 0
token: ${{ secrets.CLOUD_JAVA_BOT_TOKEN }}
- - uses: googleapis/sdk-platform-java/.github/scripts@v2.55.1
+ - uses: googleapis/sdk-platform-java/.github/scripts@v2.56.2
if: env.SHOULD_RUN == 'true'
with:
base_ref: ${{ github.base_ref }}
diff --git a/.github/workflows/renovate_config_check.yaml b/.github/workflows/renovate_config_check.yaml
index 7c5ec7865e1..47b9e87c98b 100644
--- a/.github/workflows/renovate_config_check.yaml
+++ b/.github/workflows/renovate_config_check.yaml
@@ -7,7 +7,7 @@ on:
jobs:
renovate_bot_config_validation:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- name: Checkout code
@@ -16,7 +16,7 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v4
with:
- node-version: '20'
+ node-version: '22'
- name: Install Renovate and Config Validator
run: |
diff --git a/.github/workflows/unmanaged_dependency_check.yaml b/.github/workflows/unmanaged_dependency_check.yaml
index 56e895c73d5..eaf32470034 100644
--- a/.github/workflows/unmanaged_dependency_check.yaml
+++ b/.github/workflows/unmanaged_dependency_check.yaml
@@ -17,6 +17,6 @@ jobs:
# repository
.kokoro/build.sh
- name: Unmanaged dependency check
- uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.45.1
+ uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.46.2
with:
bom-path: google-cloud-spanner-bom/pom.xml
diff --git a/.github/workflows/update_generation_config.yaml b/.github/workflows/update_generation_config.yaml
index f15c807853d..cd2d5fd5a8e 100644
--- a/.github/workflows/update_generation_config.yaml
+++ b/.github/workflows/update_generation_config.yaml
@@ -28,6 +28,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
+ fetch-depth: 0
token: ${{ secrets.CLOUD_JAVA_BOT_TOKEN }}
- name: Update params in generation config to latest
shell: bash
@@ -36,7 +37,8 @@ jobs:
[ -z "$(git config user.email)" ] && git config --global user.email "cloud-java-bot@google.com"
[ -z "$(git config user.name)" ] && git config --global user.name "cloud-java-bot"
bash .github/scripts/update_generation_config.sh \
- --base_branch "${base_branch}"\
+ --base_branch "${base_branch}" \
--repo ${{ github.repository }}
env:
GH_TOKEN: ${{ secrets.CLOUD_JAVA_BOT_TOKEN }}
+
diff --git a/.kokoro/build.sh b/.kokoro/build.sh
index 9782a77e507..0656ab0e424 100755
--- a/.kokoro/build.sh
+++ b/.kokoro/build.sh
@@ -82,7 +82,7 @@ test)
RETURN_CODE=$?
;;
lint)
- mvn com.coveo:fmt-maven-plugin:check
+ mvn com.spotify.fmt:fmt-maven-plugin:check
RETURN_CODE=$?
;;
javadoc)
@@ -98,9 +98,8 @@ integration)
-Dclirr.skip=true \
-Denforcer.skip=true \
-Dmaven.main.skip=true \
- -Dspanner.gce.config.server_url=https://staging-wrenchworks.sandbox.googleapis.com \
- -Dspanner.gce.config.project_id=span-cloud-testing \
- -Dspanner.testenv.instance=projects/span-cloud-testing/instances/java-client-integration-tests \
+ -Dspanner.gce.config.project_id=gcloud-devel \
+ -Dspanner.testenv.instance=projects/gcloud-devel/instances/java-client-integration-tests \
-fae \
verify
RETURN_CODE=$?
@@ -114,7 +113,6 @@ integration-directpath-enabled)
-Dclirr.skip=true \
-Denforcer.skip=true \
-Dmaven.main.skip=true \
- -Dspanner.gce.config.server_url=https://staging-wrenchworks.sandbox.googleapis.com \
-Dspanner.testenv.instance=projects/span-cloud-testing/instances/spanner-java-client-directpath \
-Dspanner.gce.config.project_id=span-cloud-testing \
-fae \
@@ -130,9 +128,8 @@ integration-multiplexed-sessions-enabled)
-Dclirr.skip=true \
-Denforcer.skip=true \
-Dmaven.main.skip=true \
- -Dspanner.gce.config.server_url=https://staging-wrenchworks.sandbox.googleapis.com \
- -Dspanner.gce.config.project_id=span-cloud-testing \
- -Dspanner.testenv.instance=projects/span-cloud-testing/instances/java-client-integration-tests-multiplexed-sessions \
+ -Dspanner.gce.config.project_id=gcloud-devel \
+ -Dspanner.testenv.instance=projects/gcloud-devel/instances/java-client-integration-tests-multiplexed-sessions \
-fae \
verify
RETURN_CODE=$?
diff --git a/.kokoro/presubmit/graalvm-native.cfg b/.kokoro/presubmit/graalvm-native-a.cfg
similarity index 77%
rename from .kokoro/presubmit/graalvm-native.cfg
rename to .kokoro/presubmit/graalvm-native-a.cfg
index fb1979a33ba..d0c29521333 100644
--- a/.kokoro/presubmit/graalvm-native.cfg
+++ b/.kokoro/presubmit/graalvm-native-a.cfg
@@ -3,7 +3,7 @@
# Configure the docker image for kokoro-trampoline.
env_vars: {
key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.45.1"
+ value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.46.2" # {x-version-update:google-cloud-shared-dependencies:current}
}
env_vars: {
@@ -31,3 +31,8 @@ env_vars: {
key: "SECRET_MANAGER_KEYS"
value: "java-it-service-account"
}
+
+env_vars: {
+ key: "IT_SERVICE_ACCOUNT_EMAIL"
+ value: "it-service-account@gcloud-devel.iam.gserviceaccount.com"
+}
\ No newline at end of file
diff --git a/.kokoro/presubmit/graalvm-native-17.cfg b/.kokoro/presubmit/graalvm-native-b.cfg
similarity index 74%
rename from .kokoro/presubmit/graalvm-native-17.cfg
rename to .kokoro/presubmit/graalvm-native-b.cfg
index 07ae08a9807..962c305ff7f 100644
--- a/.kokoro/presubmit/graalvm-native-17.cfg
+++ b/.kokoro/presubmit/graalvm-native-b.cfg
@@ -3,12 +3,12 @@
# Configure the docker image for kokoro-trampoline.
env_vars: {
key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.45.1"
+ value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.46.2" # {x-version-update:google-cloud-shared-dependencies:current}
}
env_vars: {
key: "JOB_TYPE"
- value: "graalvm17"
+ value: "graalvm"
}
# TODO: remove this after we've migrated all tests and scripts
@@ -30,4 +30,9 @@ env_vars: {
env_vars: {
key: "SECRET_MANAGER_KEYS"
value: "java-it-service-account"
+}
+
+env_vars: {
+ key: "IT_SERVICE_ACCOUNT_EMAIL"
+ value: "it-service-account@gcloud-devel.iam.gserviceaccount.com"
}
\ No newline at end of file
diff --git a/.kokoro/presubmit/graalvm-native-c.cfg b/.kokoro/presubmit/graalvm-native-c.cfg
new file mode 100644
index 00000000000..87b5dff65a3
--- /dev/null
+++ b/.kokoro/presubmit/graalvm-native-c.cfg
@@ -0,0 +1,38 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+# Configure the docker image for kokoro-trampoline.
+env_vars: {
+ key: "TRAMPOLINE_IMAGE"
+ value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_c:3.46.2" # {x-version-update:google-cloud-shared-dependencies:current}
+}
+
+env_vars: {
+ key: "JOB_TYPE"
+ value: "graalvm"
+}
+
+# TODO: remove this after we've migrated all tests and scripts
+env_vars: {
+ key: "GCLOUD_PROJECT"
+ value: "gcloud-devel"
+}
+
+env_vars: {
+ key: "GOOGLE_CLOUD_PROJECT"
+ value: "gcloud-devel"
+}
+
+env_vars: {
+ key: "GOOGLE_APPLICATION_CREDENTIALS"
+ value: "secret_manager/java-it-service-account"
+}
+
+env_vars: {
+ key: "SECRET_MANAGER_KEYS"
+ value: "java-it-service-account"
+}
+
+env_vars: {
+ key: "IT_SERVICE_ACCOUNT_EMAIL"
+ value: "it-service-account@gcloud-devel.iam.gserviceaccount.com"
+}
\ No newline at end of file
diff --git a/.kokoro/presubmit/integration-directpath-enabled.cfg b/.kokoro/presubmit/integration-directpath-enabled.cfg
index c7b3d45e771..ceb3bddfa70 100644
--- a/.kokoro/presubmit/integration-directpath-enabled.cfg
+++ b/.kokoro/presubmit/integration-directpath-enabled.cfg
@@ -11,6 +11,17 @@ env_vars: {
value: "integration-directpath-enabled"
}
+# TODO: remove this after we've migrated all tests and scripts
+env_vars: {
+ key: "GCLOUD_PROJECT"
+ value: "gcloud-devel"
+}
+
+env_vars: {
+ key: "GOOGLE_CLOUD_PROJECT"
+ value: "gcloud-devel"
+}
+
env_vars: {
key: "GOOGLE_APPLICATION_CREDENTIALS"
value: "secret_manager/java-client-testing"
diff --git a/.kokoro/presubmit/integration-multiplexed-sessions-enabled.cfg b/.kokoro/presubmit/integration-multiplexed-sessions-enabled.cfg
index 28b95345f71..800e2a21558 100644
--- a/.kokoro/presubmit/integration-multiplexed-sessions-enabled.cfg
+++ b/.kokoro/presubmit/integration-multiplexed-sessions-enabled.cfg
@@ -11,14 +11,25 @@ env_vars: {
value: "integration-multiplexed-sessions-enabled"
}
+# TODO: remove this after we've migrated all tests and scripts
+env_vars: {
+ key: "GCLOUD_PROJECT"
+ value: "gcloud-devel"
+}
+
+env_vars: {
+ key: "GOOGLE_CLOUD_PROJECT"
+ value: "gcloud-devel"
+}
+
env_vars: {
key: "GOOGLE_APPLICATION_CREDENTIALS"
- value: "secret_manager/java-client-testing"
+ value: "secret_manager/java-it-service-account"
}
env_vars: {
key: "SECRET_MANAGER_KEYS"
- value: "java-client-testing"
+ value: "java-it-service-account"
}
env_vars: {
diff --git a/.kokoro/presubmit/integration.cfg b/.kokoro/presubmit/integration.cfg
index cc4e22eaf9a..5864c603e5a 100644
--- a/.kokoro/presubmit/integration.cfg
+++ b/.kokoro/presubmit/integration.cfg
@@ -11,12 +11,24 @@ env_vars: {
value: "integration"
}
+# TODO: remove this after we've migrated all tests and scripts
+env_vars: {
+ key: "GCLOUD_PROJECT"
+ value: "gcloud-devel"
+}
+
+env_vars: {
+ key: "GOOGLE_CLOUD_PROJECT"
+ value: "gcloud-devel"
+}
+
env_vars: {
key: "GOOGLE_APPLICATION_CREDENTIALS"
- value: "secret_manager/java-client-testing"
+ value: "secret_manager/java-it-service-account"
}
env_vars: {
key: "SECRET_MANAGER_KEYS"
- value: "java-client-testing"
+ value: "java-it-service-account"
}
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cd77f471251..05eee459cb8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,23 @@
# Changelog
+## [6.92.0](https://github.com/googleapis/java-spanner/compare/v6.91.1...v6.92.0) (2025-04-29)
+
+
+### Features
+
+* [Internal] client-side metrics for afe latency and connectivity error ([#3819](https://github.com/googleapis/java-spanner/issues/3819)) ([a8dba0a](https://github.com/googleapis/java-spanner/commit/a8dba0a83939fdbbc324f0a7aa6c44180462fa3a))
+* Support begin with AbortedException for manager interface ([#3835](https://github.com/googleapis/java-spanner/issues/3835)) ([5783116](https://github.com/googleapis/java-spanner/commit/578311693bed836c8916f4b4ffa0782a468c1af3))
+
+
+### Bug Fixes
+
+* **deps:** Update the Java code generator (gapic-generator-java) to 2.56.2 ([11bfd90](https://github.com/googleapis/java-spanner/commit/11bfd90daa244dbd31a76bc5a1d2e694e43fa292))
+
+
+### Dependencies
+
+* Update dependency com.google.cloud:sdk-platform-java-config to v3.46.2 ([#3836](https://github.com/googleapis/java-spanner/issues/3836)) ([2ee7f97](https://github.com/googleapis/java-spanner/commit/2ee7f971f3374b01d22e5a7f8f2483cf60c3363d))
+
## [6.91.1](https://github.com/googleapis/java-spanner/compare/v6.91.0...v6.91.1) (2025-04-21)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b65dd279c94..ff092b68e3f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -84,7 +84,7 @@ Code in this repo is formatted with
[google-java-format](https://github.com/google/google-java-format).
To run formatting on your project, you can run:
```
-mvn com.coveo:fmt-maven-plugin:format
+mvn com.spotify.fmt:fmt-maven-plugin:format
```
[1]: https://cloud.google.com/docs/authentication/getting-started#creating_a_service_account
diff --git a/README.md b/README.md
index 771cd4d60db..1bcd0fcc4e6 100644
--- a/README.md
+++ b/README.md
@@ -49,20 +49,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.57.0')
+implementation platform('com.google.cloud:libraries-bom:26.59.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.91.1'
+implementation 'com.google.cloud:google-cloud-spanner:6.92.0'
```
If you are using SBT, add this to your dependencies:
```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.91.1"
+libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.92.0"
```
## Authentication
@@ -728,7 +728,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.91.1
+[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.92.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/benchmarks/pom.xml b/benchmarks/pom.xml
index 65bb942e45f..956746838ba 100644
--- a/benchmarks/pom.xml
+++ b/benchmarks/pom.xml
@@ -24,7 +24,7 @@
com.google.cloudgoogle-cloud-spanner-parent
- 6.91.1
+ 6.92.0
diff --git a/benchmarks/src/main/java/com/google/cloud/spanner/benchmark/LatencyBenchmark.java b/benchmarks/src/main/java/com/google/cloud/spanner/benchmark/LatencyBenchmark.java
index 73683932def..46083f1fee0 100644
--- a/benchmarks/src/main/java/com/google/cloud/spanner/benchmark/LatencyBenchmark.java
+++ b/benchmarks/src/main/java/com/google/cloud/spanner/benchmark/LatencyBenchmark.java
@@ -48,7 +48,11 @@ public static void main(String[] args) throws ParseException {
String.format("projects/%s/instances/%s/databases/%s", project, instance, database);
} else {
throw new IllegalArgumentException(
- "You must either set all the environment variables SPANNER_CLIENT_BENCHMARK_GOOGLE_CLOUD_PROJECT, SPANNER_CLIENT_BENCHMARK_SPANNER_INSTANCE and SPANNER_CLIENT_BENCHMARK_SPANNER_DATABASE, or specify a value for the command line argument --database");
+ "You must either set all the environment variables"
+ + " SPANNER_CLIENT_BENCHMARK_GOOGLE_CLOUD_PROJECT,"
+ + " SPANNER_CLIENT_BENCHMARK_SPANNER_INSTANCE and"
+ + " SPANNER_CLIENT_BENCHMARK_SPANNER_DATABASE, or specify a value for the command"
+ + " line argument --database");
}
LatencyBenchmark benchmark = new LatencyBenchmark(DatabaseId.of(fullyQualifiedDatabase));
@@ -69,13 +73,15 @@ private static CommandLine parseCommandLine(String[] args) throws ParseException
"w",
"wait",
true,
- "The wait time in milliseconds between each query that is executed by each client. Defaults to 0. "
- + "Set this to for example 1000 to have each client execute 1 query per second.");
+ "The wait time in milliseconds between each query that is executed by each client. Defaults"
+ + " to 0. Set this to for example 1000 to have each client execute 1 query per"
+ + " second.");
options.addOption(
"t",
"transaction",
true,
- "The type of transaction to execute. Must be either READ_ONLY or READ_WRITE. Defaults to READ_ONLY.");
+ "The type of transaction to execute. Must be either READ_ONLY or READ_WRITE. Defaults to"
+ + " READ_ONLY.");
options.addOption("m", "multiplexed", true, "Use multiplexed sessions. Defaults to false.");
options.addOption("w", "wait", true, "Wait time in millis. Defaults to zero.");
options.addOption("name", true, "Name of this test run");
diff --git a/generation_config.yaml b/generation_config.yaml
index de040162715..287eba4ba07 100644
--- a/generation_config.yaml
+++ b/generation_config.yaml
@@ -1,6 +1,6 @@
-gapic_generator_version: 2.55.1
-googleapis_commitish: 0c142cc0ed988797104f3c934b0d5cfe0906b948
-libraries_bom_version: 26.57.0
+gapic_generator_version: 2.56.2
+googleapis_commitish: 2eec62dc7ed836c8d9f73fb313afb8f48c361bef
+libraries_bom_version: 26.59.0
libraries:
- api_shortname: spanner
name_pretty: Cloud Spanner
diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml
index 62a4814ca46..d2a9d4d1dae 100644
--- a/google-cloud-spanner-bom/pom.xml
+++ b/google-cloud-spanner-bom/pom.xml
@@ -3,12 +3,12 @@
4.0.0com.google.cloudgoogle-cloud-spanner-bom
- 6.91.1
+ 6.92.0pomcom.google.cloudsdk-platform-java-config
- 3.45.1
+ 3.46.2Google Cloud Spanner BOM
@@ -53,43 +53,43 @@
com.google.cloudgoogle-cloud-spanner
- 6.91.1
+ 6.92.0com.google.cloudgoogle-cloud-spannertest-jar
- 6.91.1
+ 6.92.0com.google.api.grpcgrpc-google-cloud-spanner-v1
- 6.91.1
+ 6.92.0com.google.api.grpcgrpc-google-cloud-spanner-admin-instance-v1
- 6.91.1
+ 6.92.0com.google.api.grpcgrpc-google-cloud-spanner-admin-database-v1
- 6.91.1
+ 6.92.0com.google.api.grpcproto-google-cloud-spanner-admin-instance-v1
- 6.91.1
+ 6.92.0com.google.api.grpcproto-google-cloud-spanner-v1
- 6.91.1
+ 6.92.0com.google.api.grpcproto-google-cloud-spanner-admin-database-v1
- 6.91.1
+ 6.92.0
diff --git a/google-cloud-spanner-executor/pom.xml b/google-cloud-spanner-executor/pom.xml
index 206fb510bd2..9fb36618452 100644
--- a/google-cloud-spanner-executor/pom.xml
+++ b/google-cloud-spanner-executor/pom.xml
@@ -5,14 +5,14 @@
4.0.0com.google.cloudgoogle-cloud-spanner-executor
- 6.91.1
+ 6.92.0jarGoogle Cloud Spanner Executorcom.google.cloudgoogle-cloud-spanner-parent
- 6.91.1
+ 6.92.0
diff --git a/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudClientExecutor.java b/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudClientExecutor.java
index 64a29ed3e61..f7fa02a9958 100644
--- a/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudClientExecutor.java
+++ b/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudClientExecutor.java
@@ -424,6 +424,7 @@ public synchronized boolean finish(Mode finishMode) throws Exception {
}
}
}
+
/**
* All the context in which SpannerActions are executed. It stores the current running transaction
* and table metadata, shared by all the action executor and protected by a lock. There will only
@@ -790,7 +791,7 @@ private synchronized Spanner getClient(boolean useMultiplexedSession) throws IOE
if (client != null) {
return client;
}
- client = getClient(/*timeoutSeconds=*/ 0, useMultiplexedSession);
+ client = getClient(/* timeoutSeconds= */ 0, useMultiplexedSession);
return client;
}
@@ -1021,7 +1022,7 @@ private Status executeAction(
return executeFinishTxn(action.getFinish(), outcomeSender, executionContext);
} else if (action.hasMutation()) {
return executeMutation(
- action.getMutation(), outcomeSender, executionContext, /*isWrite=*/ false);
+ action.getMutation(), outcomeSender, executionContext, /* isWrite= */ false);
} else if (action.hasRead()) {
return executeRead(
useMultiplexedSession, action.getRead(), outcomeSender, executionContext);
@@ -1035,7 +1036,7 @@ private Status executeAction(
return executeCloudBatchDmlUpdates(action.getBatchDml(), outcomeSender, executionContext);
} else if (action.hasWrite()) {
return executeMutation(
- action.getWrite().getMutation(), outcomeSender, executionContext, /*isWrite=*/ true);
+ action.getWrite().getMutation(), outcomeSender, executionContext, /* isWrite= */ true);
} else if (action.hasStartBatchTxn()) {
if (dbPath == null) {
throw SpannerExceptionFactory.newSpannerException(
@@ -3197,7 +3198,7 @@ private static com.google.cloud.spanner.KeyRange keyRangeProtoToCloudKeyRange(
return KeyRange.openClosed(start, end);
case OPEN_OPEN:
return KeyRange.openOpen(start, end);
- // Unreachable.
+ // Unreachable.
default:
throw SpannerExceptionFactory.newSpannerException(
ErrorCode.INVALID_ARGUMENT, "Unrecognized key range type");
@@ -3250,7 +3251,7 @@ private static com.google.cloud.spanner.Key keyProtoToCloudKey(
case BYTES:
cloudKey.append(toByteArray(part.getBytesValue()));
break;
- // Unreachable
+ // Unreachable
default:
throw SpannerExceptionFactory.newSpannerException(
ErrorCode.INVALID_ARGUMENT, "Unsupported key part type: " + type.getCode().name());
diff --git a/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudExecutor.java b/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudExecutor.java
index 537a6ed4c33..eb6502c461c 100644
--- a/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudExecutor.java
+++ b/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudExecutor.java
@@ -368,9 +368,9 @@ private Status flush() {
LOGGER.log(
Level.INFO,
String.format(
- "OutcomeSender with action ID %s for change stream %s and partition token %s is "
- + "sending data change records with the following transaction id/record sequence "
- + "combinations: %s and partition tokens: %s",
+ "OutcomeSender with action ID %s for change stream %s and partition token %s is"
+ + " sending data change records with the following transaction id/record"
+ + " sequence combinations: %s and partition tokens: %s",
this.changeStreamForQuery,
this.partitionTokenForQuery,
actionId,
diff --git a/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudExecutorImpl.java b/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudExecutorImpl.java
index 6fee10c95b6..f3de36ac7b4 100644
--- a/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudExecutorImpl.java
+++ b/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudExecutorImpl.java
@@ -91,21 +91,14 @@ public void onNext(SpannerAsyncActionRequest request) {
SessionPoolOptions.Builder sessionPoolOptionsBuilder;
if (request.getAction().getSpannerOptions().hasSessionPoolOptions()) {
sessionPoolOptionsBuilder =
- request
- .getAction()
- .getSpannerOptions()
- .getSessionPoolOptions()
- .toBuilder()
+ request.getAction().getSpannerOptions().getSessionPoolOptions().toBuilder()
.setUseMultiplexed(true);
} else {
sessionPoolOptionsBuilder = SessionPoolOptions.newBuilder().setUseMultiplexed(true);
}
SpannerOptions.Builder optionsBuilder =
- request
- .getAction()
- .getSpannerOptions()
- .toBuilder()
+ request.getAction().getSpannerOptions().toBuilder()
.setSessionPoolOptions(sessionPoolOptionsBuilder);
SpannerAction.Builder actionBuilder =
request.getAction().toBuilder().setSpannerOptions(optionsBuilder);
diff --git a/google-cloud-spanner-executor/src/test/java/com/google/cloud/spanner/executor/v1/MockSpannerExecutorProxyImpl.java b/google-cloud-spanner-executor/src/test/java/com/google/cloud/spanner/executor/v1/MockSpannerExecutorProxyImpl.java
index c06dbb5c349..98019812d20 100644
--- a/google-cloud-spanner-executor/src/test/java/com/google/cloud/spanner/executor/v1/MockSpannerExecutorProxyImpl.java
+++ b/google-cloud-spanner-executor/src/test/java/com/google/cloud/spanner/executor/v1/MockSpannerExecutorProxyImpl.java
@@ -77,7 +77,8 @@ public void onNext(SpannerAsyncActionRequest value) {
responseObserver.onError(
new IllegalArgumentException(
String.format(
- "Unrecognized response type %s for method ExecuteActionAsync, expected %s or %s",
+ "Unrecognized response type %s for method ExecuteActionAsync, expected %s"
+ + " or %s",
response == null ? "null" : response.getClass().getName(),
SpannerAsyncActionResponse.class.getName(),
Exception.class.getName())));
diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml
index d6d36b0e147..94bb5870f81 100644
--- a/google-cloud-spanner/clirr-ignored-differences.xml
+++ b/google-cloud-spanner/clirr-ignored-differences.xml
@@ -566,6 +566,38 @@
java.util.List getFloat32Array()
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ java.util.UUID getUuid(int)
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ java.util.UUID getUuid(java.lang.String)
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ java.util.List getUuidList(int)
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ java.util.List getUuidList(java.lang.String)
+
+
+ 7013
+ com/google/cloud/spanner/Value
+ java.util.UUID getUuid()
+
+
+ 7013
+ com/google/cloud/spanner/Value
+ java.util.List getUuidArray()
+
+
7012
@@ -758,6 +790,13 @@
boolean isEnableGRPCBuiltInMetrics()
+
+
+ 7012
+ com/google/cloud/spanner/SpannerOptions$SpannerEnvironment
+ boolean isEnableAFEServerTiming()
+
+
7012
@@ -899,7 +938,7 @@
com/google/cloud/spanner/connection/Connectionjava.lang.String getDefaultSequenceKind()
-
+
7012
@@ -957,4 +996,16 @@
com/google/cloud/spanner/DatabaseClientcom.google.cloud.spanner.Statement$StatementFactory getStatementFactory()
+
+
+
+ 7012
+ com/google/cloud/spanner/AsyncTransactionManager
+ com.google.cloud.spanner.AsyncTransactionManager$TransactionContextFuture beginAsync(com.google.cloud.spanner.AbortedException)
+
+
+ 7012
+ com/google/cloud/spanner/TransactionManager
+ com.google.cloud.spanner.TransactionContext begin(com.google.cloud.spanner.AbortedException)
+
diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml
index 28db749a992..d50d7da9240 100644
--- a/google-cloud-spanner/pom.xml
+++ b/google-cloud-spanner/pom.xml
@@ -3,7 +3,7 @@
4.0.0com.google.cloudgoogle-cloud-spanner
- 6.91.1
+ 6.92.0jarGoogle Cloud Spannerhttps://github.com/googleapis/java-spanner
@@ -11,7 +11,7 @@
com.google.cloudgoogle-cloud-spanner-parent
- 6.91.1
+ 6.92.0google-cloud-spanner
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbortedException.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbortedException.java
index 3e5227888d9..74e45062113 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbortedException.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbortedException.java
@@ -17,6 +17,7 @@
package com.google.cloud.spanner;
import com.google.api.gax.rpc.ApiException;
+import com.google.protobuf.ByteString;
import javax.annotation.Nullable;
/**
@@ -32,6 +33,8 @@ public class AbortedException extends SpannerException {
*/
private static final boolean IS_RETRYABLE = false;
+ private ByteString transactionID;
+
/** Private constructor. Use {@link SpannerExceptionFactory} to create instances. */
AbortedException(
DoNotConstructDirectly token, @Nullable String message, @Nullable Throwable cause) {
@@ -46,6 +49,9 @@ public class AbortedException extends SpannerException {
@Nullable ApiException apiException,
@Nullable XGoogSpannerRequestId reqId) {
super(token, ErrorCode.ABORTED, IS_RETRYABLE, message, cause, apiException, reqId);
+ if (cause instanceof AbortedException) {
+ this.transactionID = ((AbortedException) cause).getTransactionID();
+ }
}
/**
@@ -55,4 +61,12 @@ public class AbortedException extends SpannerException {
public boolean isEmulatorOnlySupportsOneTransactionException() {
return getMessage().endsWith("The emulator only supports one transaction at a time.");
}
+
+ void setTransactionID(ByteString transactionID) {
+ this.transactionID = transactionID;
+ }
+
+ ByteString getTransactionID() {
+ return this.transactionID;
+ }
}
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 47b6fba0c6c..6ea4510d3db 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
@@ -793,7 +793,7 @@ ResultSet executeQueryInternalWithOptions(
options.hasPrefetchChunks() ? options.prefetchChunks() : defaultPrefetchChunks;
final ExecuteSqlRequest.Builder request =
getExecuteSqlRequestBuilder(
- statement, queryMode, options, /* withTransactionSelector = */ false);
+ statement, queryMode, options, /* withTransactionSelector= */ false);
ResumableStreamIterator stream =
new ResumableStreamIterator(
MAX_BUFFERED_CHUNKS,
@@ -1036,7 +1036,7 @@ CloseableIterator startStream(
getTransactionChannelHint(),
isRouteToLeader());
session.markUsed(clock.instant());
- stream.setCall(call, /* withBeginTransaction = */ builder.getTransaction().hasBegin());
+ stream.setCall(call, /* withBeginTransaction= */ builder.getTransaction().hasBegin());
return stream;
}
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 49df1167bd5..fc3a5609bb1 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
@@ -38,6 +38,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
+import java.util.UUID;
import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -434,6 +435,11 @@ protected Date getDateInternal(int columnIndex) {
return currRow().getDateInternal(columnIndex);
}
+ @Override
+ protected UUID getUuidInternal(int columnIndex) {
+ return currRow().getUuidInternal(columnIndex);
+ }
+
@Override
protected Interval getIntervalInternal(int columnIndex) {
return currRow().getIntervalInternal(columnIndex);
@@ -531,6 +537,11 @@ protected List getDateListInternal(int columnIndex) {
return currRow().getDateListInternal(columnIndex);
}
+ @Override
+ protected List getUuidListInternal(int columnIndex) {
+ return currRow().getUuidListInternal(columnIndex);
+ }
+
@Override
protected List getIntervalListInternal(int columnIndex) {
return currRow().getIntervalListInternal(columnIndex);
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 e5b9fa00123..60ff4fd330e 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
@@ -28,6 +28,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.UUID;
import java.util.function.Function;
/**
@@ -67,6 +68,10 @@ protected String getPgJsonbInternal(int columnIndex) {
protected abstract Date getDateInternal(int columnIndex);
+ protected UUID getUuidInternal(int columnIndex) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
protected Interval getIntervalInternal(int columnIndex) {
throw new UnsupportedOperationException("Not implemented");
}
@@ -132,6 +137,10 @@ protected List getPgJsonbListInternal(int columnIndex) {
protected abstract List getDateListInternal(int columnIndex);
+ protected List getUuidListInternal(int columnIndex) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
protected List getIntervalListInternal(int columnIndex) {
throw new UnsupportedOperationException("Not implemented");
}
@@ -307,6 +316,19 @@ public Date getDate(String columnName) {
return getDateInternal(columnIndex);
}
+ @Override
+ public UUID getUuid(int columnIndex) {
+ checkNonNullOfType(columnIndex, Type.uuid(), columnIndex);
+ return getUuidInternal(columnIndex);
+ }
+
+ @Override
+ public UUID getUuid(String columnName) {
+ final int columnIndex = getColumnIndex(columnName);
+ checkNonNullOfType(columnIndex, Type.uuid(), columnName);
+ return getUuidInternal(columnIndex);
+ }
+
@Override
public Interval getInterval(int columnIndex) {
checkNonNullOfType(columnIndex, Type.interval(), columnIndex);
@@ -604,6 +626,19 @@ public List getDateList(String columnName) {
return getDateListInternal(columnIndex);
}
+ @Override
+ public List getUuidList(int columnIndex) {
+ checkNonNullOfType(columnIndex, Type.array(Type.uuid()), columnIndex);
+ return getUuidListInternal(columnIndex);
+ }
+
+ @Override
+ public List getUuidList(String columnName) {
+ final int columnIndex = getColumnIndex(columnName);
+ checkNonNullOfType(columnIndex, Type.array(Type.uuid()), columnName);
+ return getUuidListInternal(columnIndex);
+ }
+
@Override
public List getIntervalList(int columnIndex) {
checkNonNullOfType(columnIndex, Type.array(Type.interval()), columnIndex);
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncResultSetImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncResultSetImpl.java
index d980c90f78c..e53e4db94b6 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncResultSetImpl.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncResultSetImpl.java
@@ -91,6 +91,7 @@ private enum State {
private final BlockingDeque buffer;
private Struct currentRow;
+
/** Supplies the underlying synchronous {@link ResultSet} that will be producing the rows. */
private final Supplier delegateResultSet;
@@ -137,11 +138,13 @@ private enum State {
* production of rows that are put into the buffer is only paused once the buffer is full.
*/
private volatile CountDownLatch pausedLatch = new CountDownLatch(1);
+
/**
* This variable is used to pause the producer when the buffer is full and the consumer needs some
* time to catch up.
*/
private volatile CountDownLatch bufferConsumptionLatch = new CountDownLatch(0);
+
/**
* This variable is used to pause the producer when all rows have been put into the buffer, but
* the consumer (the callback) has not yet received and processed all rows.
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java
index c6ead432046..bb5140c4755 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java
@@ -170,6 +170,21 @@ interface AsyncTransactionFunction {
*/
TransactionContextFuture beginAsync();
+ /**
+ * Initializes a new read-write transaction that is a retry of a previously aborted transaction.
+ * This method must be called before performing any operations, and it can only be invoked once
+ * per transaction lifecycle.
+ *
+ *
This method should only be used when multiplexed sessions are enabled to create a retry for
+ * a previously aborted transaction. This method can be used instead of {@link
+ * #resetForRetryAsync()} to create a retry. Using this method or {@link #resetForRetryAsync()}
+ * will have the same effect. You must pass in the {@link AbortedException} from the previous
+ * attempt to preserve the transaction's priority.
+ *
+ *
For regular sessions, this behaves the same as {@link #beginAsync()}.
+ */
+ TransactionContextFuture beginAsync(AbortedException exception);
+
/**
* Rolls back the currently active transaction. In most cases there should be no need to call this
* explicitly since {@link #close()} would automatically roll back any active transaction.
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 1578de87cdb..c5599a749f6 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
@@ -76,14 +76,27 @@ public ApiFuture closeAsync() {
@Override
public TransactionContextFutureImpl beginAsync() {
Preconditions.checkState(txn == null, "begin can only be called once");
- return new TransactionContextFutureImpl(this, internalBeginAsync(true));
+ return new TransactionContextFutureImpl(this, internalBeginAsync(true, ByteString.EMPTY));
}
- private ApiFuture internalBeginAsync(boolean firstAttempt) {
+ @Override
+ public TransactionContextFutureImpl beginAsync(AbortedException exception) {
+ Preconditions.checkState(txn == null, "begin can only be called once");
+ Preconditions.checkNotNull(exception, "AbortedException from the previous attempt is required");
+ ByteString abortedTransactionId =
+ exception.getTransactionID() != null ? exception.getTransactionID() : ByteString.EMPTY;
+ return new TransactionContextFutureImpl(this, internalBeginAsync(true, abortedTransactionId));
+ }
+
+ private ApiFuture internalBeginAsync(
+ boolean firstAttempt, ByteString abortedTransactionID) {
txnState = TransactionState.STARTED;
// Determine the latest transactionId when using a multiplexed session.
ByteString multiplexedSessionPreviousTransactionId = ByteString.EMPTY;
+ if (firstAttempt && session.getIsMultiplexed()) {
+ multiplexedSessionPreviousTransactionId = abortedTransactionID;
+ }
if (txn != null && session.getIsMultiplexed() && !firstAttempt) {
// Use the current transactionId if available, otherwise fallback to the previous aborted
// transactionId.
@@ -93,7 +106,7 @@ private ApiFuture internalBeginAsync(boolean firstAttempt) {
txn =
session.newTransaction(
- options, /* previousTransactionId = */ multiplexedSessionPreviousTransactionId);
+ options, /* previousTransactionId= */ multiplexedSessionPreviousTransactionId);
if (firstAttempt) {
session.setActive(this);
}
@@ -187,7 +200,7 @@ public TransactionContextFuture resetForRetryAsync() {
throw new IllegalStateException(
"resetForRetry can only be called if the previous attempt aborted");
}
- return new TransactionContextFutureImpl(this, internalBeginAsync(false));
+ return new TransactionContextFutureImpl(this, internalBeginAsync(false, ByteString.EMPTY));
}
@Override
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClient.java
index 45e38989be6..2d12179bc91 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClient.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClient.java
@@ -67,5 +67,6 @@ public interface BatchClient {
*/
default String getDatabaseRole() {
throw new UnsupportedOperationException("method should be overwritten");
- };
+ }
+ ;
}
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsConstant.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsConstant.java
index 050484ae66e..2e0266cec5c 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsConstant.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsConstant.java
@@ -27,6 +27,7 @@
import io.opentelemetry.sdk.metrics.InstrumentType;
import io.opentelemetry.sdk.metrics.View;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -39,6 +40,9 @@ public class BuiltInMetricsConstant {
static final String SPANNER_METER_NAME = "spanner-java";
static final String GRPC_METER_NAME = "grpc-java";
static final String GFE_LATENCIES_NAME = "gfe_latencies";
+ static final String AFE_LATENCIES_NAME = "afe_latencies";
+ static final String GFE_CONNECTIVITY_ERROR_NAME = "gfe_connectivity_error_count";
+ static final String AFE_CONNECTIVITY_ERROR_NAME = "afe_connectivity_error_count";
static final String OPERATION_LATENCIES_NAME = "operation_latencies";
static final String ATTEMPT_LATENCIES_NAME = "attempt_latencies";
static final String OPERATION_LATENCY_NAME = "operation_latency";
@@ -52,7 +56,10 @@ public class BuiltInMetricsConstant {
ATTEMPT_LATENCIES_NAME,
OPERATION_COUNT_NAME,
ATTEMPT_COUNT_NAME,
- GFE_LATENCIES_NAME)
+ GFE_LATENCIES_NAME,
+ AFE_LATENCIES_NAME,
+ GFE_CONNECTIVITY_ERROR_NAME,
+ AFE_CONNECTIVITY_ERROR_NAME)
.stream()
.map(m -> METER_NAME + '/' + m)
.collect(Collectors.toSet());
@@ -110,14 +117,14 @@ public class BuiltInMetricsConstant {
static final Set GRPC_LB_RLS_ATTRIBUTES =
ImmutableSet.of("grpc.lb.rls.data_plane_target", "grpc.lb.pick_result");
+ static List BUCKET_BOUNDARIES =
+ ImmutableList.of(
+ 0.0, 0.5, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0,
+ 16.0, 17.0, 18.0, 19.0, 20.0, 25.0, 30.0, 40.0, 50.0, 65.0, 80.0, 100.0, 130.0, 160.0,
+ 200.0, 250.0, 300.0, 400.0, 500.0, 650.0, 800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0,
+ 50000.0, 100000.0, 200000.0, 400000.0, 800000.0, 1600000.0, 3200000.0);
static Aggregation AGGREGATION_WITH_MILLIS_HISTOGRAM =
- Aggregation.explicitBucketHistogram(
- ImmutableList.of(
- 0.0, 0.5, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0,
- 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 25.0, 30.0, 40.0, 50.0, 65.0, 80.0, 100.0, 130.0,
- 160.0, 200.0, 250.0, 300.0, 400.0, 500.0, 650.0, 800.0, 1000.0, 2000.0, 5000.0,
- 10000.0, 20000.0, 50000.0, 100000.0, 200000.0, 400000.0, 800000.0, 1600000.0,
- 3200000.0));
+ Aggregation.explicitBucketHistogram(BUCKET_BOUNDARIES);
static final Collection GRPC_METRICS_ENABLED_BY_DEFAULT =
ImmutableList.of(
@@ -145,14 +152,6 @@ static Map getAllViews() {
BuiltInMetricsConstant.AGGREGATION_WITH_MILLIS_HISTOGRAM,
InstrumentType.HISTOGRAM,
"ms");
- defineView(
- views,
- BuiltInMetricsConstant.SPANNER_METER_NAME,
- BuiltInMetricsConstant.GFE_LATENCIES_NAME,
- BuiltInMetricsConstant.GFE_LATENCIES_NAME,
- BuiltInMetricsConstant.AGGREGATION_WITH_MILLIS_HISTOGRAM,
- InstrumentType.HISTOGRAM,
- "ms");
defineView(
views,
BuiltInMetricsConstant.GAX_METER_NAME,
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsProvider.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsProvider.java
index 888eff90b58..d35c69b499e 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsProvider.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsProvider.java
@@ -84,7 +84,8 @@ OpenTelemetry getOrCreateOpenTelemetry(
} catch (IOException ex) {
logger.log(
Level.WARNING,
- "Unable to get OpenTelemetry object for client side metrics, will skip exporting client side metrics",
+ "Unable to get OpenTelemetry object for client side metrics, will skip exporting client"
+ + " side metrics",
ex);
return null;
}
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsRecorder.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsRecorder.java
index a12da470b61..d8ee9fc416e 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsRecorder.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsRecorder.java
@@ -23,6 +23,7 @@
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.metrics.DoubleHistogram;
+import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.Meter;
import java.util.Map;
@@ -35,6 +36,9 @@
class BuiltInMetricsRecorder extends OpenTelemetryMetricsRecorder {
private final DoubleHistogram gfeLatencyRecorder;
+ private final DoubleHistogram afeLatencyRecorder;
+ private final LongCounter gfeHeaderMissingCountRecorder;
+ private final LongCounter afeHeaderMissingCountRecorder;
/**
* Creates the following instruments for the following metrics:
@@ -57,8 +61,31 @@ class BuiltInMetricsRecorder extends OpenTelemetryMetricsRecorder {
meter
.histogramBuilder(serviceName + '/' + BuiltInMetricsConstant.GFE_LATENCIES_NAME)
.setDescription(
- "Latency between Google's network receiving an RPC and reading back the first byte of the response")
+ "Latency between Google's network receiving an RPC and reading back the first byte"
+ + " of the response")
.setUnit("ms")
+ .setExplicitBucketBoundariesAdvice(BuiltInMetricsConstant.BUCKET_BOUNDARIES)
+ .build();
+ this.afeLatencyRecorder =
+ meter
+ .histogramBuilder(serviceName + '/' + BuiltInMetricsConstant.AFE_LATENCIES_NAME)
+ .setDescription(
+ "Latency between Spanner API Frontend receiving an RPC and starting to write back"
+ + " the response.")
+ .setExplicitBucketBoundariesAdvice(BuiltInMetricsConstant.BUCKET_BOUNDARIES)
+ .setUnit("ms")
+ .build();
+ this.gfeHeaderMissingCountRecorder =
+ meter
+ .counterBuilder(serviceName + '/' + BuiltInMetricsConstant.GFE_CONNECTIVITY_ERROR_NAME)
+ .setDescription("Number of requests that failed to reach the Google network.")
+ .setUnit("1")
+ .build();
+ this.afeHeaderMissingCountRecorder =
+ meter
+ .counterBuilder(serviceName + '/' + BuiltInMetricsConstant.AFE_CONNECTIVITY_ERROR_NAME)
+ .setDescription("Number of requests that failed to reach the Spanner API Frontend.")
+ .setUnit("1")
.build();
}
@@ -69,8 +96,25 @@ class BuiltInMetricsRecorder extends OpenTelemetryMetricsRecorder {
* @param gfeLatency Attempt Latency in ms
* @param attributes Map of the attributes to store
*/
- void recordGFELatency(double gfeLatency, Map attributes) {
- gfeLatencyRecorder.record(gfeLatency, toOtelAttributes(attributes));
+ void recordServerTimingHeaderMetrics(
+ Long gfeLatency,
+ Long afeLatency,
+ Long gfeHeaderMissingCount,
+ Long afeHeaderMissingCount,
+ Map attributes) {
+ io.opentelemetry.api.common.Attributes otelAttributes = toOtelAttributes(attributes);
+ if (gfeLatency != null) {
+ gfeLatencyRecorder.record(gfeLatency, otelAttributes);
+ }
+ if (gfeHeaderMissingCount > 0) {
+ gfeHeaderMissingCountRecorder.add(gfeHeaderMissingCount, otelAttributes);
+ }
+ if (afeLatency != null) {
+ afeLatencyRecorder.record(afeLatency, otelAttributes);
+ }
+ if (afeHeaderMissingCount > 0) {
+ afeHeaderMissingCountRecorder.add(afeHeaderMissingCount, otelAttributes);
+ }
}
Attributes toOtelAttributes(Map attributes) {
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsTracer.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsTracer.java
index 6faff5ad6d7..488cf3890c3 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsTracer.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsTracer.java
@@ -37,8 +37,10 @@ class BuiltInMetricsTracer extends MetricsTracer implements ApiTracer {
private final BuiltInMetricsRecorder builtInOpenTelemetryMetricsRecorder;
// These are RPC specific attributes and pertain to a specific API Trace
private final Map attributes = new HashMap<>();
-
private Long gfeLatency = null;
+ private Long afeLatency = null;
+ private long gfeHeaderMissingCount = 0;
+ private long afeHeaderMissingCount = 0;
BuiltInMetricsTracer(
MethodName methodName, BuiltInMetricsRecorder builtInOpenTelemetryMetricsRecorder) {
@@ -54,10 +56,9 @@ class BuiltInMetricsTracer extends MetricsTracer implements ApiTracer {
@Override
public void attemptSucceeded() {
super.attemptSucceeded();
- if (gfeLatency != null) {
- attributes.put(STATUS_ATTRIBUTE, StatusCode.Code.OK.toString());
- builtInOpenTelemetryMetricsRecorder.recordGFELatency(gfeLatency, attributes);
- }
+ attributes.put(STATUS_ATTRIBUTE, StatusCode.Code.OK.toString());
+ builtInOpenTelemetryMetricsRecorder.recordServerTimingHeaderMetrics(
+ gfeLatency, afeLatency, gfeHeaderMissingCount, afeHeaderMissingCount, attributes);
}
/**
@@ -67,10 +68,9 @@ public void attemptSucceeded() {
@Override
public void attemptCancelled() {
super.attemptCancelled();
- if (gfeLatency != null) {
- attributes.put(STATUS_ATTRIBUTE, StatusCode.Code.CANCELLED.toString());
- builtInOpenTelemetryMetricsRecorder.recordGFELatency(gfeLatency, attributes);
- }
+ attributes.put(STATUS_ATTRIBUTE, StatusCode.Code.CANCELLED.toString());
+ builtInOpenTelemetryMetricsRecorder.recordServerTimingHeaderMetrics(
+ gfeLatency, afeLatency, gfeHeaderMissingCount, afeHeaderMissingCount, attributes);
}
/**
@@ -84,10 +84,9 @@ public void attemptCancelled() {
@Override
public void attemptFailedDuration(Throwable error, java.time.Duration delay) {
super.attemptFailedDuration(error, delay);
- if (gfeLatency != null) {
- attributes.put(STATUS_ATTRIBUTE, extractStatus(error));
- builtInOpenTelemetryMetricsRecorder.recordGFELatency(gfeLatency, attributes);
- }
+ attributes.put(STATUS_ATTRIBUTE, extractStatus(error));
+ builtInOpenTelemetryMetricsRecorder.recordServerTimingHeaderMetrics(
+ gfeLatency, afeLatency, gfeHeaderMissingCount, afeHeaderMissingCount, attributes);
}
/**
@@ -100,10 +99,9 @@ public void attemptFailedDuration(Throwable error, java.time.Duration delay) {
@Override
public void attemptFailedRetriesExhausted(Throwable error) {
super.attemptFailedRetriesExhausted(error);
- if (gfeLatency != null) {
- attributes.put(STATUS_ATTRIBUTE, extractStatus(error));
- builtInOpenTelemetryMetricsRecorder.recordGFELatency(gfeLatency, attributes);
- }
+ attributes.put(STATUS_ATTRIBUTE, extractStatus(error));
+ builtInOpenTelemetryMetricsRecorder.recordServerTimingHeaderMetrics(
+ gfeLatency, afeLatency, gfeHeaderMissingCount, afeHeaderMissingCount, attributes);
}
/**
@@ -116,21 +114,33 @@ public void attemptFailedRetriesExhausted(Throwable error) {
@Override
public void attemptPermanentFailure(Throwable error) {
super.attemptPermanentFailure(error);
- if (gfeLatency != null) {
- attributes.put(STATUS_ATTRIBUTE, extractStatus(error));
- builtInOpenTelemetryMetricsRecorder.recordGFELatency(gfeLatency, attributes);
- }
+ attributes.put(STATUS_ATTRIBUTE, extractStatus(error));
+ builtInOpenTelemetryMetricsRecorder.recordServerTimingHeaderMetrics(
+ gfeLatency, afeLatency, gfeHeaderMissingCount, afeHeaderMissingCount, attributes);
}
void recordGFELatency(Long gfeLatency) {
this.gfeLatency = gfeLatency;
}
+ void recordAFELatency(Long afeLatency) {
+ this.afeLatency = afeLatency;
+ }
+
+ void recordGfeHeaderMissingCount(Long value) {
+ this.gfeHeaderMissingCount = value;
+ }
+
+ void recordAfeHeaderMissingCount(Long value) {
+ this.afeHeaderMissingCount = value;
+ }
+
@Override
public void addAttributes(Map attributes) {
super.addAttributes(attributes);
this.attributes.putAll(attributes);
- };
+ }
+ ;
@Override
public void addAttributes(String key, String value) {
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitResponse.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitResponse.java
index 3ebd8f55315..43179972b9b 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitResponse.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitResponse.java
@@ -41,7 +41,9 @@ public Timestamp getCommitTimestamp() {
return Timestamp.fromProto(proto.getCommitTimestamp());
}
- /** @return true if the {@link CommitResponse} includes {@link CommitStats} */
+ /**
+ * @return true if the {@link CommitResponse} includes {@link CommitStats}
+ */
public boolean hasCommitStats() {
return proto.hasCommitStats();
}
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CompositeTracer.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CompositeTracer.java
index 5268e9046f8..afc202342d8 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CompositeTracer.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CompositeTracer.java
@@ -198,4 +198,28 @@ public void recordGFELatency(Long gfeLatency) {
}
}
}
+
+ public void recordGfeHeaderMissingCount(Long value) {
+ for (ApiTracer child : children) {
+ if (child instanceof BuiltInMetricsTracer) {
+ ((BuiltInMetricsTracer) child).recordGfeHeaderMissingCount(value);
+ }
+ }
+ }
+
+ public void recordAFELatency(Long afeLatency) {
+ for (ApiTracer child : children) {
+ if (child instanceof BuiltInMetricsTracer) {
+ ((BuiltInMetricsTracer) child).recordAFELatency(afeLatency);
+ }
+ }
+ }
+
+ public void recordAfeHeaderMissingCount(Long value) {
+ for (ApiTracer child : children) {
+ if (child instanceof BuiltInMetricsTracer) {
+ ((BuiltInMetricsTracer) child).recordAfeHeaderMissingCount(value);
+ }
+ }
+ }
}
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 8e0e07c457b..624aba7547c 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
@@ -53,11 +53,11 @@ class DatabaseClientImpl implements DatabaseClient {
this(
"",
pool,
- /* useMultiplexedSessionBlindWrite = */ false,
- /* multiplexedSessionDatabaseClient = */ null,
+ /* useMultiplexedSessionBlindWrite= */ false,
+ /* multiplexedSessionDatabaseClient= */ null,
/* useMultiplexedSessionPartitionedOps= */ false,
tracer,
- /* useMultiplexedSessionForRW = */ false,
+ /* useMultiplexedSessionForRW= */ false,
Attributes.empty());
}
@@ -66,11 +66,11 @@ class DatabaseClientImpl implements DatabaseClient {
this(
clientId,
pool,
- /* useMultiplexedSessionBlindWrite = */ false,
- /* multiplexedSessionDatabaseClient = */ null,
+ /* useMultiplexedSessionBlindWrite= */ false,
+ /* multiplexedSessionDatabaseClient= */ null,
/* useMultiplexedSessionPartitionedOps= */ false,
tracer,
- /* useMultiplexedSessionForRW = */ false,
+ /* useMultiplexedSessionForRW= */ false,
Attributes.empty());
}
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DelayedAsyncTransactionManager.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DelayedAsyncTransactionManager.java
index 56b874e4a87..530670960ca 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DelayedAsyncTransactionManager.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DelayedAsyncTransactionManager.java
@@ -50,6 +50,11 @@ public TransactionContextFuture beginAsync() {
return getAsyncTransactionManager().beginAsync();
}
+ @Override
+ public TransactionContextFuture beginAsync(AbortedException exception) {
+ return getAsyncTransactionManager().beginAsync(exception);
+ }
+
@Override
public ApiFuture rollbackAsync() {
return getAsyncTransactionManager().rollbackAsync();
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DelayedMultiplexedSessionTransaction.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DelayedMultiplexedSessionTransaction.java
index 57ffca6bce6..debb07d7af4 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DelayedMultiplexedSessionTransaction.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DelayedMultiplexedSessionTransaction.java
@@ -64,7 +64,7 @@ public ReadContext singleUse() {
this.sessionFuture,
sessionReference ->
new MultiplexedSessionTransaction(
- client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse = */ true)
+ client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse= */ true)
.singleUse(),
MoreExecutors.directExecutor()));
}
@@ -76,7 +76,7 @@ public ReadContext singleUse(TimestampBound bound) {
this.sessionFuture,
sessionReference ->
new MultiplexedSessionTransaction(
- client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse = */ true)
+ client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse= */ true)
.singleUse(bound),
MoreExecutors.directExecutor()));
}
@@ -88,7 +88,7 @@ public ReadOnlyTransaction singleUseReadOnlyTransaction() {
this.sessionFuture,
sessionReference ->
new MultiplexedSessionTransaction(
- client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse = */ true)
+ client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse= */ true)
.singleUseReadOnlyTransaction(),
MoreExecutors.directExecutor()));
}
@@ -100,7 +100,7 @@ public ReadOnlyTransaction singleUseReadOnlyTransaction(TimestampBound bound) {
this.sessionFuture,
sessionReference ->
new MultiplexedSessionTransaction(
- client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse = */ true)
+ client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse= */ true)
.singleUseReadOnlyTransaction(bound),
MoreExecutors.directExecutor()));
}
@@ -112,7 +112,7 @@ public ReadOnlyTransaction readOnlyTransaction() {
this.sessionFuture,
sessionReference ->
new MultiplexedSessionTransaction(
- client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse = */ false)
+ client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse= */ false)
.readOnlyTransaction(),
MoreExecutors.directExecutor()));
}
@@ -124,7 +124,7 @@ public ReadOnlyTransaction readOnlyTransaction(TimestampBound bound) {
this.sessionFuture,
sessionReference ->
new MultiplexedSessionTransaction(
- client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse = */ false)
+ client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse= */ false)
.readOnlyTransaction(bound),
MoreExecutors.directExecutor()));
}
@@ -139,7 +139,7 @@ public CommitResponse writeAtLeastOnceWithOptions(
SessionReference sessionReference = getSessionReference();
try (MultiplexedSessionTransaction transaction =
new MultiplexedSessionTransaction(
- client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse = */ true)) {
+ client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse= */ true)) {
return transaction.writeAtLeastOnceWithOptions(mutations, options);
}
}
@@ -151,7 +151,7 @@ public Timestamp write(Iterable mutations) throws SpannerException {
SessionReference sessionReference = getSessionReference();
try (MultiplexedSessionTransaction transaction =
new MultiplexedSessionTransaction(
- client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse = */ false)) {
+ client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse= */ false)) {
return transaction.write(mutations);
}
}
@@ -164,7 +164,7 @@ public CommitResponse writeWithOptions(Iterable mutations, Transaction
SessionReference sessionReference = getSessionReference();
try (MultiplexedSessionTransaction transaction =
new MultiplexedSessionTransaction(
- client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse = */ false)) {
+ client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse= */ false)) {
return transaction.writeWithOptions(mutations, options);
}
}
@@ -180,7 +180,7 @@ public ServerStream batchWriteAtLeastOnce(
SessionReference sessionReference = getSessionReference();
try (MultiplexedSessionTransaction transaction =
new MultiplexedSessionTransaction(
- client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse = */ true)) {
+ client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse= */ true)) {
return transaction.batchWriteAtLeastOnce(mutationGroups, options);
}
}
@@ -196,7 +196,7 @@ public TransactionRunner readWriteTransaction(TransactionOption... options) {
span,
sessionReference,
NO_CHANNEL_HINT,
- /* singleUse = */ false,
+ /* singleUse= */ false,
this.sessionPool)
.readWriteTransaction(options),
MoreExecutors.directExecutor()));
@@ -209,7 +209,7 @@ public TransactionManager transactionManager(TransactionOption... options) {
this.sessionFuture,
sessionReference ->
new MultiplexedSessionTransaction(
- client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse = */ false)
+ client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse= */ false)
.transactionManager(options),
MoreExecutors.directExecutor()));
}
@@ -221,7 +221,7 @@ public AsyncRunner runAsync(TransactionOption... options) {
this.sessionFuture,
sessionReference ->
new MultiplexedSessionTransaction(
- client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse = */ false)
+ client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse= */ false)
.runAsync(options),
MoreExecutors.directExecutor()));
}
@@ -233,7 +233,7 @@ public AsyncTransactionManager transactionManagerAsync(TransactionOption... opti
this.sessionFuture,
sessionReference ->
new MultiplexedSessionTransaction(
- client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse = */ false)
+ client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse= */ false)
.transactionManagerAsync(options),
MoreExecutors.directExecutor()));
}
@@ -260,7 +260,7 @@ private SessionReference getSessionReference() {
public long executePartitionedUpdate(Statement stmt, UpdateOption... options) {
SessionReference sessionReference = getSessionReference();
return new MultiplexedSessionTransaction(
- client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse = */ true)
+ client, span, sessionReference, NO_CHANNEL_HINT, /* singleUse= */ true)
.executePartitionedUpdate(stmt, options);
}
}
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DelayedTransactionManager.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DelayedTransactionManager.java
index 29eae6477fc..96400e9e9bb 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DelayedTransactionManager.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DelayedTransactionManager.java
@@ -49,6 +49,11 @@ public TransactionContext begin() {
return getTransactionManager().begin();
}
+ @Override
+ public TransactionContext begin(AbortedException exception) {
+ return getTransactionManager().begin(exception);
+ }
+
@Override
public void commit() {
getTransactionManager().commit();
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DmlBatchUpdateCountVerificationFailedException.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DmlBatchUpdateCountVerificationFailedException.java
index f8c334ddbd5..c2c94598168 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DmlBatchUpdateCountVerificationFailedException.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DmlBatchUpdateCountVerificationFailedException.java
@@ -38,13 +38,15 @@ public class DmlBatchUpdateCountVerificationFailedException extends AbortedExcep
super(
token,
String.format(
- "Actual update counts that were returned during execution do not match the previously returned update counts.\n"
+ "Actual update counts that were returned during execution do not match the previously"
+ + " returned update counts.\n"
+ "Expected: %s\n"
+ "Actual: %s\n"
- + "Set auto_batch_dml_update_count_verification to false to skip this verification.",
+ + "Set auto_batch_dml_update_count_verification to false to skip this"
+ + " verification.",
Arrays.stream(expected).mapToObj(Long::toString).collect(Collectors.joining()),
Arrays.stream(actual).mapToObj(Long::toString).collect(Collectors.joining())),
- /* cause = */ null);
+ /* cause= */ null);
this.expected = expected;
this.actual = actual;
}
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ErrorCode.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ErrorCode.java
index 9896cc8aec9..07771a3faca 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ErrorCode.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ErrorCode.java
@@ -69,7 +69,9 @@ Status getGrpcStatus() {
return this.code.toStatus();
}
- /** @return the corresponding gRPC status code of this {@link ErrorCode}. */
+ /**
+ * @return the corresponding gRPC status code of this {@link ErrorCode}.
+ */
public Status.Code getGrpcStatusCode() {
return this.code;
}
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java
index c747c2a5908..839202bb9fe 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java
@@ -26,6 +26,7 @@
import com.google.protobuf.ProtocolMessageEnum;
import java.math.BigDecimal;
import java.util.List;
+import java.util.UUID;
import java.util.function.Function;
/** Forwarding implements of StructReader */
@@ -231,6 +232,18 @@ public Date getDate(String columnName) {
return delegate.get().getDate(columnName);
}
+ @Override
+ public UUID getUuid(int columnIndex) {
+ checkValidState();
+ return delegate.get().getUuid(columnIndex);
+ }
+
+ @Override
+ public UUID getUuid(String columnName) {
+ checkValidState();
+ return delegate.get().getUuid(columnName);
+ }
+
@Override
public Interval getInterval(int columnIndex) {
checkValidState();
@@ -421,6 +434,18 @@ public List getDateList(String columnName) {
return delegate.get().getDateList(columnName);
}
+ @Override
+ public List getUuidList(int columnIndex) {
+ checkValidState();
+ return delegate.get().getUuidList(columnIndex);
+ }
+
+ @Override
+ public List getUuidList(String columnName) {
+ checkValidState();
+ return delegate.get().getUuidList(columnName);
+ }
+
@Override
public List getIntervalList(int columnIndex) {
checkValidState();
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/GrpcStruct.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/GrpcStruct.java
index bd783366997..6f0a54039b7 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/GrpcStruct.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/GrpcStruct.java
@@ -49,6 +49,7 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -131,6 +132,9 @@ private Object writeReplace() {
case DATE:
builder.set(fieldName).to((Date) value);
break;
+ case UUID:
+ builder.set(fieldName).to((UUID) value);
+ break;
case INTERVAL:
builder.set(fieldName).to((Interval) value);
break;
@@ -187,6 +191,9 @@ private Object writeReplace() {
case DATE:
builder.set(fieldName).toDateArray((Iterable) value);
break;
+ case UUID:
+ builder.set(fieldName).toUuidArray((Iterable) value);
+ break;
case INTERVAL:
builder.set(fieldName).toIntervalArray((Iterable) value);
break;
@@ -216,8 +223,8 @@ private Object writeReplace() {
type,
rowData,
decodeMode,
- /* rowDecoded = */ false,
- /* colDecoded = */ decodeMode == DecodeMode.LAZY_PER_COL
+ /* rowDecoded= */ false,
+ /* colDecoded= */ decodeMode == DecodeMode.LAZY_PER_COL
? new BitSet(type.getStructFields().size())
: null);
}
@@ -304,6 +311,9 @@ private static Object decodeValue(Type fieldType, com.google.protobuf.Value prot
case DATE:
checkType(fieldType, proto, KindCase.STRING_VALUE);
return Date.parseDate(proto.getStringValue());
+ case UUID:
+ checkType(fieldType, proto, KindCase.STRING_VALUE);
+ return UUID.fromString(proto.getStringValue());
case INTERVAL:
checkType(fieldType, proto, KindCase.STRING_VALUE);
return Interval.parseFromString(proto.getStringValue());
@@ -356,6 +366,7 @@ static Object decodeArrayValue(Type elementType, ListValue listValue) {
case BYTES:
case TIMESTAMP:
case DATE:
+ case UUID:
case INTERVAL:
case STRUCT:
case PROTO:
@@ -415,12 +426,12 @@ public boolean isNull(int columnIndex) {
protected T getProtoMessageInternal(int columnIndex, T message) {
Preconditions.checkNotNull(
message,
- "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value.");
+ "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter"
+ + " value.");
ensureDecoded(columnIndex);
try {
return (T)
- message
- .toBuilder()
+ message.toBuilder()
.mergeFrom(
Base64.getDecoder()
.wrap(
@@ -513,6 +524,12 @@ protected Date getDateInternal(int columnIndex) {
return (Date) rowData.get(columnIndex);
}
+ @Override
+ protected UUID getUuidInternal(int columnIndex) {
+ ensureDecoded(columnIndex);
+ return (UUID) rowData.get(columnIndex);
+ }
+
@Override
protected Interval getIntervalInternal(int columnIndex) {
ensureDecoded(columnIndex);
@@ -640,6 +657,8 @@ protected Value getValueInternal(int columnIndex) {
return Value.timestamp(isNull ? null : getTimestampInternal(columnIndex));
case DATE:
return Value.date(isNull ? null : getDateInternal(columnIndex));
+ case UUID:
+ return Value.uuid(isNull ? null : getUuidInternal(columnIndex));
case INTERVAL:
return Value.interval(isNull ? null : getIntervalInternal(columnIndex));
case STRUCT:
@@ -682,6 +701,8 @@ protected Value getValueInternal(int columnIndex) {
return Value.timestampArray(isNull ? null : getTimestampListInternal(columnIndex));
case DATE:
return Value.dateArray(isNull ? null : getDateListInternal(columnIndex));
+ case UUID:
+ return Value.uuidArray(isNull ? null : getUuidListInternal(columnIndex));
case INTERVAL:
return Value.intervalArray(isNull ? null : getIntervalListInternal(columnIndex));
case STRUCT:
@@ -787,7 +808,8 @@ protected List getProtoMessageListInternal(
int columnIndex, T message) {
Preconditions.checkNotNull(
message,
- "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value.");
+ "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter"
+ + " value.");
ensureDecoded(columnIndex);
List bytesArray = (List) rowData.get(columnIndex);
@@ -800,8 +822,7 @@ protected List getProtoMessageListInternal(
} else {
protoMessagesList.add(
(T)
- message
- .toBuilder()
+ message.toBuilder()
.mergeFrom(
Base64.getDecoder()
.wrap(
@@ -867,6 +888,13 @@ protected List getDateListInternal(int columnIndex) {
return Collections.unmodifiableList((List) rowData.get(columnIndex));
}
+ @Override
+ @SuppressWarnings("unchecked") // We know ARRAY produces a List.
+ protected List getUuidListInternal(int columnIndex) {
+ ensureDecoded(columnIndex);
+ return Collections.unmodifiableList((List) rowData.get(columnIndex));
+ }
+
@Override
@SuppressWarnings("unchecked") // We know ARRAY produces a List.
protected List getIntervalListInternal(int columnIndex) {
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/GrpcValueIterator.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/GrpcValueIterator.java
index 24c431eec31..5d1a884a9de 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/GrpcValueIterator.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/GrpcValueIterator.java
@@ -183,7 +183,9 @@ boolean isWithBeginTransaction() {
return stream.isWithBeginTransaction();
}
- /** @param a is a mutable list and b will be concatenated into a. */
+ /**
+ * @param a is a mutable list and b will be concatenated into a.
+ */
private void concatLists(List a, List b) {
if (a.size() == 0 || b.size() == 0) {
a.addAll(b);
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceNotFoundException.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceNotFoundException.java
index 18961569bbf..5139cd3894e 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceNotFoundException.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceNotFoundException.java
@@ -37,6 +37,7 @@ public class InstanceNotFoundException extends ResourceNotFoundException {
@Nullable Throwable cause) {
this(token, message, resourceInfo, cause, null, null);
}
+
/** Private constructor. Use {@link SpannerExceptionFactory} to create instances. */
InstanceNotFoundException(
DoNotConstructDirectly token,
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Key.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Key.java
index 3467052605a..83c0db0a3e1 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Key.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Key.java
@@ -31,6 +31,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.UUID;
import javax.annotation.Nullable;
/**
@@ -70,6 +71,7 @@ private Key(List