diff --git a/.github/workflows/auto-release.yaml b/.github/workflows/auto-release.yaml index 18d92e5a28..0cda6b04f7 100644 --- a/.github/workflows/auto-release.yaml +++ b/.github/workflows/auto-release.yaml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest if: contains(github.head_ref, 'release-please') steps: - - uses: actions/github-script@v7 + - uses: actions/github-script@v8 with: github-token: ${{secrets.YOSHI_APPROVER_TOKEN}} debug: true diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8717db17ae..e2e6881c78 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - java: [11, 17, 21] + java: [11, 17, 21, 25] steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v3 diff --git a/.github/workflows/hermetic_library_generation.yaml b/.github/workflows/hermetic_library_generation.yaml index 6d1265ec78..0e85f80524 100644 --- a/.github/workflows/hermetic_library_generation.yaml +++ b/.github/workflows/hermetic_library_generation.yaml @@ -32,12 +32,12 @@ jobs: else echo "SHOULD_RUN=true" >> $GITHUB_ENV fi - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 if: env.SHOULD_RUN == 'true' with: fetch-depth: 0 token: ${{ secrets.CLOUD_JAVA_BOT_TOKEN }} - - uses: googleapis/sdk-platform-java/.github/scripts@v2.62.2 + - uses: googleapis/sdk-platform-java/.github/scripts@v2.62.3 if: env.SHOULD_RUN == 'true' with: base_ref: ${{ github.base_ref }} diff --git a/.github/workflows/integration-tests-against-emulator-with-regular-session.yaml b/.github/workflows/integration-tests-against-emulator-with-regular-session.yaml index 06e7e416d9..371620cf5a 100644 --- a/.github/workflows/integration-tests-against-emulator-with-regular-session.yaml +++ b/.github/workflows/integration-tests-against-emulator-with-regular-session.yaml @@ -16,18 +16,18 @@ jobs: - 9020:9020 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: stCarolas/setup-maven@v5 with: maven-version: 3.8.1 # Build with JDK 11 and run tests with JDK 8 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: java-version: 11 distribution: temurin - name: Compiling main library run: .kokoro/build.sh - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: java-version: 8 distribution: temurin diff --git a/.github/workflows/integration-tests-against-emulator.yaml b/.github/workflows/integration-tests-against-emulator.yaml index f4ac97a8fe..cd677dd1a6 100644 --- a/.github/workflows/integration-tests-against-emulator.yaml +++ b/.github/workflows/integration-tests-against-emulator.yaml @@ -16,18 +16,18 @@ jobs: - 9020:9020 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: stCarolas/setup-maven@v5 with: maven-version: 3.8.1 # Build with JDK 11 and run tests with JDK 8 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: java-version: 11 distribution: temurin - name: Compiling main library run: .kokoro/build.sh - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: java-version: 8 distribution: temurin diff --git a/.github/workflows/unmanaged_dependency_check.yaml b/.github/workflows/unmanaged_dependency_check.yaml index e646d4e2c2..6040b5ceee 100644 --- a/.github/workflows/unmanaged_dependency_check.yaml +++ b/.github/workflows/unmanaged_dependency_check.yaml @@ -5,8 +5,8 @@ jobs: unmanaged_dependency_check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: distribution: temurin java-version: 11 @@ -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.52.2 + uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.52.3 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 a7e14bb483..59e39834dd 100644 --- a/.github/workflows/update_generation_config.yaml +++ b/.github/workflows/update_generation_config.yaml @@ -26,7 +26,7 @@ jobs: # the branch into which the pull request is merged base_branch: main steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 token: ${{ secrets.CLOUD_JAVA_BOT_TOKEN }} diff --git a/.kokoro/presubmit/graalvm-native-a.cfg b/.kokoro/presubmit/graalvm-native-a.cfg index 5554627daa..b7567eeb7d 100644 --- a/.kokoro/presubmit/graalvm-native-a.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.52.2" # {x-version-update:google-cloud-shared-dependencies:current} + value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.52.3" # {x-version-update:google-cloud-shared-dependencies:current} } env_vars: { diff --git a/.kokoro/presubmit/graalvm-native-b.cfg b/.kokoro/presubmit/graalvm-native-b.cfg index 1089437409..c7205f0abd 100644 --- a/.kokoro/presubmit/graalvm-native-b.cfg +++ b/.kokoro/presubmit/graalvm-native-b.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_b:3.52.2" # {x-version-update:google-cloud-shared-dependencies:current} + value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.52.3" # {x-version-update:google-cloud-shared-dependencies:current} } env_vars: { diff --git a/.kokoro/presubmit/graalvm-native-c.cfg b/.kokoro/presubmit/graalvm-native-c.cfg index 5465e51923..f6ab8976a5 100644 --- a/.kokoro/presubmit/graalvm-native-c.cfg +++ b/.kokoro/presubmit/graalvm-native-c.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_c:3.52.2" # {x-version-update:google-cloud-shared-dependencies:current} + value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_c:3.52.3" # {x-version-update:google-cloud-shared-dependencies:current} } env_vars: { diff --git a/CHANGELOG.md b/CHANGELOG.md index 56dd8b0ae4..7fd2983cfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,37 @@ # Changelog +## [6.102.0](https://github.com/googleapis/java-spanner/compare/v6.101.1...v6.102.0) (2025-10-08) + + +### Features + +* Add connection property for gRPC interceptor provider ([#4149](https://github.com/googleapis/java-spanner/issues/4149)) ([deb8dff](https://github.com/googleapis/java-spanner/commit/deb8dff6c01c37a3158e8f4a28ef5e821d10092a)) +* Support statement_timeout in connection url ([#4103](https://github.com/googleapis/java-spanner/issues/4103)) ([542c6aa](https://github.com/googleapis/java-spanner/commit/542c6aa63bfdd526070f14cb76921dd34527c1f9)) + + +### Bug Fixes + +* Automatically set default_sequence_kind for CREATE SEQUENCE ([#4105](https://github.com/googleapis/java-spanner/issues/4105)) ([3beea6a](https://github.com/googleapis/java-spanner/commit/3beea6ac4eb53b70db34e0a2d2e33e56f450c88b)) +* **deps:** Update the Java code generator (gapic-generator-java) to 2.62.3 ([7047a3a](https://github.com/googleapis/java-spanner/commit/7047a3ae31aae51e9e23758fe004b93855a0ee4b)) + + +### Dependencies + +* Update actions/checkout action to v5 ([#4069](https://github.com/googleapis/java-spanner/issues/4069)) ([4c88eb9](https://github.com/googleapis/java-spanner/commit/4c88eb91a321aa718f957296012f9e7501c7caec)) +* Update actions/checkout action to v5 ([#4106](https://github.com/googleapis/java-spanner/issues/4106)) ([14ebdb3](https://github.com/googleapis/java-spanner/commit/14ebdb35c33442c4e0f70d63dce3425edb730525)) +* Update actions/setup-java action to v5 ([#4071](https://github.com/googleapis/java-spanner/issues/4071)) ([e23134a](https://github.com/googleapis/java-spanner/commit/e23134a2f864e8abd2890ac3a81ff6b668afbe63)) +* Update all dependencies ([#4099](https://github.com/googleapis/java-spanner/issues/4099)) ([b262edc](https://github.com/googleapis/java-spanner/commit/b262edcfc4713bb64986bc4acd3f02b69d3367f8)) +* Update dependency com.google.api.grpc:grpc-google-cloud-monitoring-v3 to v3.77.0 ([#4117](https://github.com/googleapis/java-spanner/issues/4117)) ([2451ca2](https://github.com/googleapis/java-spanner/commit/2451ca2abe1dd2de3907b88e8d18beab1a15a634)) +* Update dependency com.google.api.grpc:proto-google-cloud-monitoring-v3 to v3.77.0 ([#4143](https://github.com/googleapis/java-spanner/issues/4143)) ([6c9dc26](https://github.com/googleapis/java-spanner/commit/6c9dc26330cf66f196adc2203323a482e08f0325)) +* Update dependency com.google.api.grpc:proto-google-cloud-trace-v1 to v2.76.0 ([#4144](https://github.com/googleapis/java-spanner/issues/4144)) ([d566a42](https://github.com/googleapis/java-spanner/commit/d566a4295be018070169ba082a018394a2e60b45)) +* Update dependency com.google.cloud:google-cloud-monitoring to v3.77.0 ([#4145](https://github.com/googleapis/java-spanner/issues/4145)) ([8917c05](https://github.com/googleapis/java-spanner/commit/8917c054410e4035d6d4e201e43599d5ddc1fadd)) +* Update dependency com.google.cloud:google-cloud-monitoring to v3.77.0 ([#4146](https://github.com/googleapis/java-spanner/issues/4146)) ([4ebea1a](https://github.com/googleapis/java-spanner/commit/4ebea1adf726069084087ce46900f3174658055c)) +* Update dependency com.google.cloud:google-cloud-trace to v2.76.0 ([#4147](https://github.com/googleapis/java-spanner/issues/4147)) ([4b1d4af](https://github.com/googleapis/java-spanner/commit/4b1d4af19336e493af38a1e58c95786da3892d34)) +* Update dependency com.google.cloud:google-cloud-trace to v2.76.0 ([#4148](https://github.com/googleapis/java-spanner/issues/4148)) ([8f91a89](https://github.com/googleapis/java-spanner/commit/8f91a894771653213b6fcded5795349ad7ea6724)) +* Update dependency com.google.cloud:sdk-platform-java-config to v3.52.3 ([#4107](https://github.com/googleapis/java-spanner/issues/4107)) ([8a8a042](https://github.com/googleapis/java-spanner/commit/8a8a042494b092b3dddd0c9606a63197d8a23555)) +* Update dependency org.json:json to v20250517 ([#3881](https://github.com/googleapis/java-spanner/issues/3881)) ([5658c83](https://github.com/googleapis/java-spanner/commit/5658c8378aa2e8028d4ef7dfaf94b647f33cd812)) +* Update googleapis/sdk-platform-java action to v2.62.3 ([#4108](https://github.com/googleapis/java-spanner/issues/4108)) ([65913ec](https://github.com/googleapis/java-spanner/commit/65913ec0638fec4ea536cf42f8fe25460133f68e)) + ## [6.101.1](https://github.com/googleapis/java-spanner/compare/v6.101.0...v6.101.1) (2025-09-26) diff --git a/README.md b/README.md index 73f923590a..6fc3af24e7 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ If you are using Maven without the BOM, add this to your dependencies: com.google.cloud google-cloud-spanner - 6.99.0 + 6.101.1 ``` @@ -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.68.0') +implementation platform('com.google.cloud:libraries-bom:26.69.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.101.0' +implementation 'com.google.cloud:google-cloud-spanner:6.101.1' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.101.0" +libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.101.1" ``` ## Authentication @@ -731,7 +731,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.101.0 +[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.101.1 [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 47598f1c64..f976c0b4dd 100644 --- a/benchmarks/pom.xml +++ b/benchmarks/pom.xml @@ -24,7 +24,7 @@ com.google.cloud google-cloud-spanner-parent - 6.101.1 + 6.102.0 @@ -34,7 +34,7 @@ UTF-8 UTF-8 2.10.1 - 1.47.0 + 1.54.1 @@ -49,17 +49,17 @@ com.google.cloud.opentelemetry exporter-trace - 0.33.0 + 0.36.0 com.google.cloud.opentelemetry exporter-metrics - 0.33.0 + 0.36.0 com.google.cloud google-cloud-monitoring - 3.63.0 + 3.77.0 @@ -87,17 +87,10 @@ re2j 1.8 - - io.opentelemetry - opentelemetry-bom - 1.50.0 - pom - import - com.google.cloud google-cloud-spanner - 6.99.0 + 6.101.1 com.google.auto.value @@ -118,7 +111,7 @@ commons-cli commons-cli - 1.9.0 + 1.10.0 @@ -133,7 +126,7 @@ org.codehaus.mojo exec-maven-plugin - 3.5.0 + 3.6.1 com.google.cloud.spanner.benchmark.LatencyBenchmark false @@ -142,7 +135,7 @@ com.spotify.fmt fmt-maven-plugin - 2.27 + 2.29 diff --git a/generation_config.yaml b/generation_config.yaml index f1a9a7abab..bdd59f3570 100644 --- a/generation_config.yaml +++ b/generation_config.yaml @@ -1,6 +1,6 @@ -gapic_generator_version: 2.62.2 -googleapis_commitish: 329ace5e3712a2e37d6159d4dcd998d8c73f261e -libraries_bom_version: 26.68.0 +gapic_generator_version: 2.62.3 +googleapis_commitish: d9a16f2feec3d0f03899e48007a02ce154fc919d +libraries_bom_version: 26.69.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 33e4677274..92de4f39b0 100644 --- a/google-cloud-spanner-bom/pom.xml +++ b/google-cloud-spanner-bom/pom.xml @@ -3,12 +3,12 @@ 4.0.0 com.google.cloud google-cloud-spanner-bom - 6.101.1 + 6.102.0 pom com.google.cloud sdk-platform-java-config - 3.52.2 + 3.52.3 Google Cloud Spanner BOM @@ -53,43 +53,43 @@ com.google.cloud google-cloud-spanner - 6.101.1 + 6.102.0 com.google.cloud google-cloud-spanner test-jar - 6.101.1 + 6.102.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.101.1 + 6.102.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.101.1 + 6.102.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.101.1 + 6.102.0 com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.101.1 + 6.102.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.101.1 + 6.102.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.101.1 + 6.102.0 @@ -100,7 +100,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.14.0 + 3.14.1 1.8 1.8 diff --git a/google-cloud-spanner-executor/pom.xml b/google-cloud-spanner-executor/pom.xml index edabbdac62..c9b0170a06 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.101.1 + 6.102.0 jar Google Cloud Spanner Executor com.google.cloud google-cloud-spanner-parent - 6.101.1 + 6.102.0 @@ -64,7 +64,7 @@ com.google.cloud google-cloud-trace - 2.62.0 + 2.76.0 io.grpc @@ -137,7 +137,7 @@ com.google.api.grpc proto-google-cloud-trace-v1 - 2.62.0 + 2.76.0 com.google.api.grpc @@ -170,12 +170,12 @@ commons-cli commons-cli - 1.9.0 + 1.10.0 commons-io commons-io - 2.19.0 + 2.20.0 @@ -202,7 +202,7 @@ org.apache.maven.surefire surefire-junit4 - 3.5.3 + 3.5.4 test @@ -267,7 +267,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.5.3 + 3.5.4 diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index 9bb8709315..67f9a08602 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.101.1 + 6.102.0 jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 6.101.1 + 6.102.0 google-cloud-spanner @@ -269,12 +269,18 @@ com.google.cloud google-cloud-monitoring - 3.63.0 + 3.77.0 com.google.api.grpc proto-google-cloud-monitoring-v3 - 3.63.0 + 3.77.0 + + + com.google.api.grpc + grpc-google-cloud-monitoring-v3 + 3.77.0 + test com.google.auth @@ -428,7 +434,7 @@ org.json json - 20250107 + 20250517 test @@ -475,13 +481,13 @@ com.google.cloud google-cloud-trace - 2.62.0 + 2.76.0 test com.google.api.grpc proto-google-cloud-trace-v1 - 2.62.0 + 2.76.0 test @@ -522,7 +528,11 @@ -classpath org.openjdk.jmh.Main - ${benchmark.name} + ${benchmark.name} + -rf + JSON + -rff + jmh-results.json @@ -544,6 +554,21 @@ + + validate-benchmark + + + + org.codehaus.mojo + exec-maven-plugin + + com.google.cloud.spanner.benchmarking.BenchmarkValidator + test + + + + + slow-tests diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MissingDefaultSequenceKindException.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MissingDefaultSequenceKindException.java index f153cf6fe4..80e0ac7efa 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MissingDefaultSequenceKindException.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MissingDefaultSequenceKindException.java @@ -29,8 +29,8 @@ public class MissingDefaultSequenceKindException extends SpannerException { private static final Pattern PATTERN = Pattern.compile( - "The sequence kind of an identity column .+ is not specified\\. Please specify the" - + " sequence kind explicitly or set the database option `default_sequence_kind`\\."); + ".*Please specify the sequence kind explicitly or set the database option" + + " `default_sequence_kind`\\."); /** Private constructor. Use {@link SpannerExceptionFactory} to create instances. */ MissingDefaultSequenceKindException( diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerCloudMonitoringExporter.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerCloudMonitoringExporter.java index 40202a0eef..bedf660007 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerCloudMonitoringExporter.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerCloudMonitoringExporter.java @@ -22,8 +22,10 @@ import com.google.api.gax.core.CredentialsProvider; import com.google.api.gax.core.FixedCredentialsProvider; import com.google.api.gax.core.NoCredentialsProvider; +import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; import com.google.api.gax.rpc.PermissionDeniedException; import com.google.auth.Credentials; +import com.google.cloud.NoCredentials; import com.google.cloud.monitoring.v3.MetricServiceClient; import com.google.cloud.monitoring.v3.MetricServiceSettings; import com.google.common.annotations.VisibleForTesting; @@ -34,6 +36,7 @@ import com.google.monitoring.v3.ProjectName; import com.google.monitoring.v3.TimeSeries; import com.google.protobuf.Empty; +import io.grpc.ManagedChannelBuilder; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.data.AggregationTemporality; @@ -79,7 +82,7 @@ static SpannerCloudMonitoringExporter create( throws IOException { MetricServiceSettings.Builder settingsBuilder = MetricServiceSettings.newBuilder(); CredentialsProvider credentialsProvider; - if (credentials == null) { + if (credentials == null || credentials instanceof NoCredentials) { credentialsProvider = NoCredentialsProvider.create(); } else { credentialsProvider = FixedCredentialsProvider.create(credentials); @@ -92,6 +95,19 @@ static SpannerCloudMonitoringExporter create( settingsBuilder.setUniverseDomain(universeDomain); } + if (System.getProperty("jmh.monitoring-server-port") != null) { + settingsBuilder.setTransportChannelProvider( + InstantiatingGrpcChannelProvider.newBuilder() + .setCredentials(NoCredentials.getInstance()) + .setChannelConfigurator( + managedChannelBuilder -> + ManagedChannelBuilder.forAddress( + "0.0.0.0", + Integer.parseInt(System.getProperty("jmh.monitoring-server-port"))) + .usePlaintext()) + .build()); + } + Duration timeout = Duration.ofMinutes(1); // TODO: createServiceTimeSeries needs special handling if the request failed. Leaving // it as not retried for now. 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 8f5baca64f..34fad2a69c 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 @@ -149,12 +149,56 @@ static final class ClosedException extends RuntimeException { this.dbAdminClient = new DatabaseAdminClientImpl(options.getProjectId(), gapicRpc); this.instanceClient = new InstanceAdminClientImpl(options.getProjectId(), gapicRpc, dbAdminClient); + logSpannerOptions(options); } SpannerImpl(SpannerOptions options) { this(options.getSpannerRpcV1(), options); } + private void logSpannerOptions(SpannerOptions options) { + logger.log( + Level.INFO, + "Spanner options: " + + "\nProject ID: " + + options.getProjectId() + + "\nHost: " + + options.getHost() + + "\nNum gRPC channels: " + + options.getNumChannels() + + "\nLeader aware routing enabled: " + + options.isLeaderAwareRoutingEnabled() + + "\nDirect access enabled: " + + options.isEnableDirectAccess() + + "\nActive Tracing Framework: " + + SpannerOptions.getActiveTracingFramework() + + "\nAPI tracing enabled: " + + options.isEnableApiTracing() + + "\nExtended tracing enabled: " + + options.isEnableExtendedTracing() + + "\nEnd to end tracing enabled: " + + options.isEndToEndTracingEnabled() + + "\nBuilt-in metrics enabled: " + + options.isEnableBuiltInMetrics()); + if (options.getSessionPoolOptions() != null) { + logger.log( + Level.INFO, + "Session pool options: " + + "\nSession pool min sessions: " + + options.getSessionPoolOptions().getMinSessions() + + "\nSession pool max sessions: " + + options.getSessionPoolOptions().getMaxSessions() + + "\nMultiplexed sessions enabled: " + + options.getSessionPoolOptions().getUseMultiplexedSession() + + "\nMultiplexed sessions enabled for RW: " + + options.getSessionPoolOptions().getUseMultiplexedSessionForRW() + + "\nMultiplexed sessions enabled for blind write: " + + options.getSessionPoolOptions().getUseMultiplexedSessionBlindWrite() + + "\nMultiplexed sessions enabled for partitioned ops: " + + options.getSessionPoolOptions().getUseMultiplexedSessionPartitionedOps()); + } + } + /** Returns the {@link SpannerRpc} of this {@link SpannerImpl} instance. */ SpannerRpc getRpc() { return gapicRpc; 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 af4ed58bad..765114dc68 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 @@ -2013,6 +2013,11 @@ public CallCredentialsProvider getCallCredentialsProvider() { } private boolean usesNoCredentials() { + // When JMH is enabled, we need to enable built-in metrics + if (System.getProperty("jmh.enabled") != null + && System.getProperty("jmh.enabled").equals("true")) { + return false; + } return Objects.equals(getCredentials(), NoCredentials.getInstance()); } 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 57916aae58..3d796af4f0 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 @@ -20,6 +20,7 @@ import static com.google.cloud.spanner.connection.ReadOnlyStalenessUtil.toChronoUnit; import com.google.api.gax.core.CredentialsProvider; +import com.google.api.gax.grpc.GrpcInterceptorProvider; import com.google.cloud.spanner.Dialect; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.Options.RpcPriority; @@ -827,6 +828,54 @@ public CredentialsProvider convert(String credentialsProviderName) { } } + static class GrpcInterceptorProviderConverter + implements ClientSideStatementValueConverter { + static final GrpcInterceptorProviderConverter INSTANCE = new GrpcInterceptorProviderConverter(); + + private GrpcInterceptorProviderConverter() {} + + @Override + public Class getParameterClass() { + return GrpcInterceptorProvider.class; + } + + @Override + public GrpcInterceptorProvider convert(String interceptorProviderName) { + if (!Strings.isNullOrEmpty(interceptorProviderName)) { + try { + Class clazz = + (Class) Class.forName(interceptorProviderName); + Constructor constructor = + clazz.getDeclaredConstructor(); + return constructor.newInstance(); + } catch (ClassNotFoundException classNotFoundException) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.INVALID_ARGUMENT, + "Unknown or invalid GrpcInterceptorProvider class name: " + interceptorProviderName, + classNotFoundException); + } catch (NoSuchMethodException noSuchMethodException) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.INVALID_ARGUMENT, + "GrpcInterceptorProvider " + + interceptorProviderName + + " does not have a public no-arg constructor.", + noSuchMethodException); + } catch (InvocationTargetException + | InstantiationException + | IllegalAccessException exception) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.INVALID_ARGUMENT, + "Failed to create an instance of " + + interceptorProviderName + + ": " + + exception.getMessage(), + exception); + } + } + return null; + } + } + /** Converter for converting strings to {@link Dialect} values. */ static class DialectConverter implements ClientSideStatementValueConverter { static final DialectConverter INSTANCE = new DialectConverter(); 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 8ae4c0fc5f..07ded59d85 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 @@ -44,6 +44,7 @@ import static com.google.cloud.spanner.connection.ConnectionProperties.RETURN_COMMIT_STATS; import static com.google.cloud.spanner.connection.ConnectionProperties.RPC_PRIORITY; import static com.google.cloud.spanner.connection.ConnectionProperties.SAVEPOINT_SUPPORT; +import static com.google.cloud.spanner.connection.ConnectionProperties.STATEMENT_TIMEOUT; import static com.google.cloud.spanner.connection.ConnectionProperties.TRACING_PREFIX; import static com.google.cloud.spanner.connection.ConnectionProperties.TRANSACTION_TIMEOUT; @@ -345,7 +346,7 @@ static UnitOfWorkType of(TransactionMode transactionMode) { && getDialect() == Dialect.POSTGRESQL ? Type.TRANSACTIONAL : Type.NON_TRANSACTIONAL)); - + setInitialStatementTimeout(options.getInitialConnectionPropertyValue(STATEMENT_TIMEOUT)); // (Re)set the state of the connection to the default. setDefaultTransactionOptions(getDefaultIsolationLevel()); } @@ -379,6 +380,7 @@ && getDialect() == Dialect.POSTGRESQL new ConnectionState( options.getInitialConnectionPropertyValues(), Suppliers.ofInstance(Type.NON_TRANSACTIONAL)); + setInitialStatementTimeout(options.getInitialConnectionPropertyValue(STATEMENT_TIMEOUT)); setReadOnly(options.isReadOnly()); setAutocommit(options.isAutocommit()); setReturnCommitStats(options.isReturnCommitStats()); @@ -390,6 +392,21 @@ public Spanner getSpanner() { return this.spanner; } + private void setInitialStatementTimeout(Duration duration) { + if (duration == null || duration.isZero()) { + return; + } + com.google.protobuf.Duration protoDuration = + com.google.protobuf.Duration.newBuilder() + .setSeconds(duration.getSeconds()) + .setNanos(duration.getNano()) + .build(); + TimeUnit unit = + ReadOnlyStalenessUtil.getAppropriateTimeUnit( + new ReadOnlyStalenessUtil.DurationGetter(protoDuration)); + setStatementTimeout(ReadOnlyStalenessUtil.durationToUnits(protoDuration, unit), unit); + } + private DdlClient createDdlClient() { return DdlClient.newBuilder() .setDatabaseAdminClient(spanner.getDatabaseAdminClient()) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java index b12cd21fa7..741989ff5b 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java @@ -33,6 +33,7 @@ import static com.google.cloud.spanner.connection.ConnectionProperties.ENABLE_EXTENDED_TRACING; import static com.google.cloud.spanner.connection.ConnectionProperties.ENCODED_CREDENTIALS; import static com.google.cloud.spanner.connection.ConnectionProperties.ENDPOINT; +import static com.google.cloud.spanner.connection.ConnectionProperties.GRPC_INTERCEPTOR_PROVIDER; import static com.google.cloud.spanner.connection.ConnectionProperties.IS_EXPERIMENTAL_HOST; import static com.google.cloud.spanner.connection.ConnectionProperties.LENIENT; import static com.google.cloud.spanner.connection.ConnectionProperties.MAX_COMMIT_DELAY; @@ -59,6 +60,7 @@ import com.google.api.core.InternalApi; import com.google.api.gax.core.CredentialsProvider; +import com.google.api.gax.grpc.GrpcInterceptorProvider; import com.google.api.gax.rpc.TransportChannelProvider; import com.google.auth.Credentials; import com.google.auth.oauth2.AccessToken; @@ -75,6 +77,7 @@ import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; +import com.google.cloud.spanner.connection.ClientSideStatementValueConverters.GrpcInterceptorProviderConverter; import com.google.cloud.spanner.connection.StatementExecutor.StatementExecutorType; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; @@ -256,6 +259,9 @@ public class ConnectionOptions { public static final String ENABLE_CHANNEL_PROVIDER_SYSTEM_PROPERTY = "ENABLE_CHANNEL_PROVIDER"; + public static final String ENABLE_GRPC_INTERCEPTOR_PROVIDER_SYSTEM_PROPERTY = + "ENABLE_GRPC_INTERCEPTOR_PROVIDER"; + /** Custom user agent string is only for other Google libraries. */ static final String USER_AGENT_PROPERTY_NAME = "userAgent"; @@ -656,19 +662,6 @@ private ConnectionOptions(Builder builder) { // Create the initial connection state from the parsed properties in the connection URL. this.initialConnectionState = new ConnectionState(connectionPropertyValues); - // Check that at most one of credentials location, encoded credentials, credentials provider and - // OUAuth token has been specified in the connection URI. - Preconditions.checkArgument( - Stream.of( - getInitialConnectionPropertyValue(CREDENTIALS_URL), - getInitialConnectionPropertyValue(ENCODED_CREDENTIALS), - getInitialConnectionPropertyValue(CREDENTIALS_PROVIDER), - getInitialConnectionPropertyValue(OAUTH_TOKEN)) - .filter(Objects::nonNull) - .count() - <= 1, - "Specify only one of credentialsUrl, encodedCredentials, credentialsProvider and OAuth" - + " token"); checkGuardedProperty( getInitialConnectionPropertyValue(ENCODED_CREDENTIALS), ENABLE_ENCODED_CREDENTIALS_SYSTEM_PROPERTY, @@ -683,6 +676,23 @@ private ConnectionOptions(Builder builder) { getInitialConnectionPropertyValue(CHANNEL_PROVIDER), ENABLE_CHANNEL_PROVIDER_SYSTEM_PROPERTY, CHANNEL_PROVIDER_PROPERTY_NAME); + checkGuardedProperty( + getInitialConnectionPropertyValue(GRPC_INTERCEPTOR_PROVIDER), + ENABLE_GRPC_INTERCEPTOR_PROVIDER_SYSTEM_PROPERTY, + GRPC_INTERCEPTOR_PROVIDER.getName()); + // Check that at most one of credentials location, encoded credentials, credentials provider and + // OUAuth token has been specified in the connection URI. + Preconditions.checkArgument( + Stream.of( + getInitialConnectionPropertyValue(CREDENTIALS_URL), + getInitialConnectionPropertyValue(ENCODED_CREDENTIALS), + getInitialConnectionPropertyValue(CREDENTIALS_PROVIDER), + getInitialConnectionPropertyValue(OAUTH_TOKEN)) + .filter(Objects::nonNull) + .count() + <= 1, + "Specify only one of credentialsUrl, encodedCredentials, credentialsProvider and OAuth" + + " token"); boolean usePlainText = getInitialConnectionPropertyValue(AUTO_CONFIG_EMULATOR) @@ -999,6 +1009,19 @@ public TransportChannelProvider getChannelProvider() { } } + String getGrpcInterceptorProviderName() { + return getInitialConnectionPropertyValue(GRPC_INTERCEPTOR_PROVIDER); + } + + /** Returns the gRPC interceptor provider that has been configured. */ + public GrpcInterceptorProvider getGrpcInterceptorProvider() { + String interceptorProvider = getInitialConnectionPropertyValue(GRPC_INTERCEPTOR_PROVIDER); + if (interceptorProvider == null) { + return null; + } + return GrpcInterceptorProviderConverter.INSTANCE.convert(interceptorProvider); + } + /** * The database role that is used for this connection. Assigning a role to a connection can be * used to for example restrict the access of a connection to a specific set of tables. diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionProperties.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionProperties.java index 3bb5d71e48..200de9bec8 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionProperties.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionProperties.java @@ -75,6 +75,7 @@ import static com.google.cloud.spanner.connection.ConnectionOptions.ENABLE_API_TRACING_PROPERTY_NAME; import static com.google.cloud.spanner.connection.ConnectionOptions.ENABLE_END_TO_END_TRACING_PROPERTY_NAME; import static com.google.cloud.spanner.connection.ConnectionOptions.ENABLE_EXTENDED_TRACING_PROPERTY_NAME; +import static com.google.cloud.spanner.connection.ConnectionOptions.ENABLE_GRPC_INTERCEPTOR_PROVIDER_SYSTEM_PROPERTY; import static com.google.cloud.spanner.connection.ConnectionOptions.ENCODED_CREDENTIALS_PROPERTY_NAME; import static com.google.cloud.spanner.connection.ConnectionOptions.ENDPOINT_PROPERTY_NAME; import static com.google.cloud.spanner.connection.ConnectionOptions.IS_EXPERIMENTAL_HOST_PROPERTY_NAME; @@ -101,6 +102,7 @@ import static com.google.cloud.spanner.connection.ConnectionProperty.castProperty; import com.google.api.gax.core.CredentialsProvider; +import com.google.api.gax.grpc.GrpcInterceptorProvider; import com.google.cloud.spanner.Dialect; import com.google.cloud.spanner.DmlBatchUpdateCountVerificationFailedException; import com.google.cloud.spanner.Options.RpcPriority; @@ -286,6 +288,23 @@ public class ConnectionProperties { null, CredentialsProviderConverter.INSTANCE, Context.STARTUP); + static final ConnectionProperty GRPC_INTERCEPTOR_PROVIDER = + create( + "grpc_interceptor_provider", + "The class name of a " + + GrpcInterceptorProvider.class.getName() + + " implementation that should be used to provide interceptors for the underlying" + + " Spanner client. This is a guarded property that can only be set if the Java" + + " System Property " + + ENABLE_GRPC_INTERCEPTOR_PROVIDER_SYSTEM_PROPERTY + + " has been set to true. This property should only be set to true on systems where" + + " an untrusted user cannot modify the connection URL, as using this property will" + + " dynamically invoke the constructor of the class specified. This means that any" + + " user that can modify the connection URL, can also dynamically invoke code on the" + + " host where the application is running.", + null, + StringValueConverter.INSTANCE, + Context.STARTUP); static final ConnectionProperty USER_AGENT = create( @@ -494,6 +513,15 @@ public class ConnectionProperties { .toArray(new ReadLockMode[0]), ReadLockModeConverter.INSTANCE, Context.USER); + static final ConnectionProperty STATEMENT_TIMEOUT = + create( + "statement_timeout", + "Adds a timeout to all statements executed on this connection. " + + "This property is only used when a statement timeout is specified.", + null, + null, + DurationConverter.INSTANCE, + Context.USER); static final ConnectionProperty TRANSACTION_TIMEOUT = create( "transaction_timeout", diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java index c1cf3ae679..e78a646f07 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java @@ -166,6 +166,7 @@ static class SpannerPoolKey { private final boolean isExperimentalHost; private final Boolean enableDirectAccess; private final String universeDomain; + private final String grpcInterceptorProvider; @VisibleForTesting static SpannerPoolKey of(ConnectionOptions options) { @@ -202,6 +203,7 @@ private SpannerPoolKey(ConnectionOptions options) throws IOException { this.isExperimentalHost = options.isExperimentalHost(); this.enableDirectAccess = options.isEnableDirectAccess(); this.universeDomain = options.getUniverseDomain(); + this.grpcInterceptorProvider = options.getGrpcInterceptorProviderName(); } @Override @@ -229,7 +231,8 @@ public boolean equals(Object o) { && Objects.equals(this.clientCertificateKey, other.clientCertificateKey) && Objects.equals(this.isExperimentalHost, other.isExperimentalHost) && Objects.equals(this.enableDirectAccess, other.enableDirectAccess) - && Objects.equals(this.universeDomain, other.universeDomain); + && Objects.equals(this.universeDomain, other.universeDomain) + && Objects.equals(this.grpcInterceptorProvider, other.grpcInterceptorProvider); } @Override @@ -253,7 +256,8 @@ public int hashCode() { this.clientCertificateKey, this.isExperimentalHost, this.enableDirectAccess, - this.universeDomain); + this.universeDomain, + this.grpcInterceptorProvider); } } @@ -426,6 +430,9 @@ Spanner createSpanner(SpannerPoolKey key, ConnectionOptions options) { if (key.universeDomain != null) { builder.setUniverseDomain(key.universeDomain); } + if (key.grpcInterceptorProvider != null) { + builder.setInterceptorProvider(options.getGrpcInterceptorProvider()); + } if (options.getConfigurator() != null) { options.getConfigurator().configure(builder); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/benchmarking/BenchmarkValidator.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/benchmarking/BenchmarkValidator.java new file mode 100644 index 0000000000..225197af6c --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/benchmarking/BenchmarkValidator.java @@ -0,0 +1,156 @@ +/* + * Copyright 2025 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.benchmarking; + +import com.google.cloud.spanner.benchmarking.BenchmarkValidator.BaselineResult.BenchmarkResult; +import com.google.cloud.spanner.benchmarking.BenchmarkValidator.BaselineResult.BenchmarkResult.Percentile; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class BenchmarkValidator { + + private final BaselineResult expectedResults; + private final List actualResults; + + public BenchmarkValidator(String baselineFile, String actualFile) { + Gson gson = new Gson(); + // Load expected result JSON from resource folder + this.expectedResults = gson.fromJson(loadJsonFromResources(baselineFile), BaselineResult.class); + // Load the actual result from current benchmarking run + this.actualResults = + gson.fromJson( + loadJsonFromFile(actualFile), + new TypeToken>() {}.getType()); + } + + void validate() { + // Validating the resultant percentile against expected percentile with allowed threshold + for (ActualBenchmarkResult actualResult : actualResults) { + BenchmarkResult expectResult = expectedResults.benchmarkResultMap.get(actualResult.benchmark); + if (expectResult == null) { + throw new ValidationException( + "Missing expected benchmark configuration for actual benchmarking"); + } + Map actualPercentilesMap = actualResult.primaryMetric.scorePercentiles; + // We will only be comparing the percentiles(p50, p90, p90) which are configured in the + // expected percentiles. This allows some checks to be disabled if required. + for (Percentile expectedPercentile : expectResult.scorePercentiles) { + String percentile = expectedPercentile.percentile; + double difference = + calculatePercentageDifference( + expectedPercentile.baseline, actualPercentilesMap.get(percentile)); + // if an absolute different in percentage is greater than allowed difference + // Then we are throwing validation error + if (Math.abs(Math.ceil(difference)) > expectedPercentile.difference) { + throw new ValidationException( + String.format( + "[%s][%s] Expected percentile %s[+/-%s] but got %s", + actualResult.benchmark, + percentile, + expectedPercentile.baseline, + expectedPercentile.difference, + actualPercentilesMap.get(percentile))); + } + } + } + } + + public static double calculatePercentageDifference(double base, double compareWith) { + if (base == 0) { + return 0.0; + } + return ((compareWith - base) / base) * 100; + } + + private String loadJsonFromFile(String file) { + try { + return new String(Files.readAllBytes(Paths.get(file))); + } catch (IOException e) { + throw new ValidationException("Failed to read file: " + file, e); + } + } + + private String loadJsonFromResources(String baselineFile) { + URL resourceUrl = getClass().getClassLoader().getResource(baselineFile); + if (resourceUrl == null) { + throw new ValidationException("File not found: " + baselineFile); + } + File file = new File(resourceUrl.getFile()); + return loadJsonFromFile(file.getAbsolutePath()); + } + + static class ActualBenchmarkResult { + String benchmark; + PrimaryMetric primaryMetric; + + static class PrimaryMetric { + Map scorePercentiles; + } + } + + static class BaselineResult { + Map benchmarkResultMap; + + static class BenchmarkResult { + List scorePercentiles; + + static class Percentile { + String percentile; + Double baseline; + Double difference; + } + } + } + + static class ValidationException extends RuntimeException { + ValidationException(String message) { + super(message); + } + + ValidationException(String message, Throwable cause) { + super(message, cause); + } + } + + private static String parseCommandLineArgs(String[] args, String key) { + if (args == null) { + return ""; + } + for (String arg : args) { + if (arg.startsWith("--" + key)) { + String[] splits = arg.split("="); + if (splits.length == 2) { + return splits[1].trim(); + } + } + } + return ""; + } + + public static void main(String[] args) { + String actualFile = parseCommandLineArgs(args, "file"); + new BenchmarkValidator("com/google/cloud/spanner/jmh/jmh-baseline.json", actualFile).validate(); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/benchmarking/MonitoringServiceImpl.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/benchmarking/MonitoringServiceImpl.java new file mode 100644 index 0000000000..aaa7387612 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/benchmarking/MonitoringServiceImpl.java @@ -0,0 +1,39 @@ +/* + * Copyright 2025 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.benchmarking; + +import com.google.monitoring.v3.CreateTimeSeriesRequest; +import com.google.monitoring.v3.MetricServiceGrpc.MetricServiceImplBase; +import com.google.protobuf.Empty; +import io.grpc.Status; +import io.grpc.stub.StreamObserver; + +class MonitoringServiceImpl extends MetricServiceImplBase { + + @Override + public void createServiceTimeSeries( + CreateTimeSeriesRequest request, StreamObserver responseObserver) { + try { + Thread.sleep(100); + responseObserver.onNext(Empty.getDefaultInstance()); + responseObserver.onCompleted(); + } catch (InterruptedException e) { + responseObserver.onError( + Status.CANCELLED.withCause(e).withDescription(e.getMessage()).asException()); + } + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/benchmarking/ReadBenchmark.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/benchmarking/ReadBenchmark.java new file mode 100644 index 0000000000..eed461fc89 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/benchmarking/ReadBenchmark.java @@ -0,0 +1,228 @@ +/* + * Copyright 2025 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.benchmarking; + +import com.google.cloud.NoCredentials; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Key; +import com.google.cloud.spanner.KeySet; +import com.google.cloud.spanner.MockSpannerServiceImpl; +import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; +import com.google.cloud.spanner.ReadContext; +import com.google.cloud.spanner.ResultSet; +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.StructType.Field; +import com.google.spanner.v1.TypeCode; +import io.grpc.ManagedChannelBuilder; +import io.grpc.Server; +import io.grpc.ServerBuilder; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Timeout; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.results.format.ResultFormatType; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +@BenchmarkMode(Mode.SampleTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@Threads(10) +@Fork(1) +public class ReadBenchmark { + + @State(Scope.Benchmark) + public static class BenchmarkState { + + // Spanner state + Spanner spanner; + DatabaseClient databaseClient; + + // gRPC server + Server gRPCServer; + Server gRPCMonitoringServer; + + // Executors for handling parallel requests by gRPC server + ExecutorService gRPCServerExecutor; + + // Table + List columns = Arrays.asList("id", "name"); + String selectQuery = "SELECT * FROM [TABLE] WHERE ID = 1"; + + @Setup(Level.Trial) + public void setup() throws IOException { + // Enable JMH system property + System.setProperty("jmh.enabled", "true"); + + // Initializing mock spanner service + MockSpannerServiceImpl mockSpannerService = new MockSpannerServiceImpl(); + mockSpannerService.setAbortProbability(0.0D); + + // Initializing mock monitoring service + MonitoringServiceImpl mockMonitoringService = new MonitoringServiceImpl(); + + // Create a thread pool to handle concurrent requests + gRPCServerExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + + // Creating Spanner Inprocess gRPC server + gRPCServer = + ServerBuilder.forPort(0) + .addService(mockSpannerService) + .executor(gRPCServerExecutor) + .build() + .start(); + + registerMocks(mockSpannerService); + + // Creating Monitoring Inprocess gRPC server + gRPCMonitoringServer = + ServerBuilder.forPort(0).addService(mockMonitoringService).build().start(); + + // Set the monitoring host port for exporter to forward requests to local netty gRPC server + System.setProperty( + "jmh.monitoring-server-port", String.valueOf(gRPCMonitoringServer.getPort())); + + spanner = + SpannerOptions.newBuilder() + .setProjectId("[PROJECT]") + .setCredentials(NoCredentials.getInstance()) + .setChannelConfigurator( + managedChannelBuilder -> + ManagedChannelBuilder.forAddress("0.0.0.0", gRPCServer.getPort()) + .usePlaintext()) + .build() + .getService(); + databaseClient = + spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE_ID]", "[DATABASE_ID]")); + } + + private void registerMocks(MockSpannerServiceImpl mockSpannerService) { + ResultSetMetadata selectMetadata = + ResultSetMetadata.newBuilder() + .setRowType( + StructType.newBuilder() + .addFields( + Field.newBuilder() + .setName("id") + .setType( + com.google.spanner.v1.Type.newBuilder() + .setCode(TypeCode.INT64) + .build()) + .build()) + .addFields( + Field.newBuilder() + .setName("name") + .setType( + com.google.spanner.v1.Type.newBuilder() + .setCode(TypeCode.STRING) + .build()) + .build()) + .build()) + .build(); + com.google.spanner.v1.ResultSet selectResultSet = + com.google.spanner.v1.ResultSet.newBuilder() + .addRows( + ListValue.newBuilder() + .addValues(com.google.protobuf.Value.newBuilder().setStringValue("1").build()) + .addValues( + com.google.protobuf.Value.newBuilder().setStringValue("[NAME]").build()) + .build()) + .setMetadata(selectMetadata) + .build(); + mockSpannerService.putStatementResult( + StatementResult.read( + "[TABLE]", KeySet.singleKey(Key.of()), this.columns, selectResultSet)); + mockSpannerService.putStatementResult( + StatementResult.query(Statement.of(this.selectQuery), selectResultSet)); + } + + @TearDown(Level.Trial) + public void tearDown() throws InterruptedException { + spanner.close(); + gRPCServer.shutdown(); + gRPCServerExecutor.shutdown(); + + // awaiting termination for servers and executors + gRPCServer.awaitTermination(10, TimeUnit.SECONDS); + gRPCServerExecutor.awaitTermination(10, TimeUnit.SECONDS); + } + } + + @Benchmark + @Warmup(time = 5, timeUnit = TimeUnit.MINUTES, iterations = 1) + @Measurement(time = 15, timeUnit = TimeUnit.MINUTES, iterations = 1) + @Timeout(time = 30, timeUnit = TimeUnit.MINUTES) + public void readBenchmark(BenchmarkState benchmarkState, Blackhole blackhole) { + try (ReadContext readContext = benchmarkState.databaseClient.singleUse()) { + try (ResultSet resultSet = + readContext.read("[TABLE]", KeySet.singleKey(Key.of("2")), benchmarkState.columns)) { + while (resultSet.next()) { + blackhole.consume(resultSet.getLong("id")); + } + } + } + } + + @Benchmark + @Warmup(time = 5, timeUnit = TimeUnit.MINUTES, iterations = 1) + @Measurement(time = 15, timeUnit = TimeUnit.MINUTES, iterations = 1) + @Timeout(time = 30, timeUnit = TimeUnit.MINUTES) + public void queryBenchmark(BenchmarkState benchmarkState, Blackhole blackhole) { + try (ReadContext readContext = benchmarkState.databaseClient.singleUse()) { + try (ResultSet resultSet = + readContext.executeQuery(Statement.of(benchmarkState.selectQuery))) { + while (resultSet.next()) { + blackhole.consume(resultSet.getLong("id")); + } + } + } + } + + public static void main(String[] args) throws RunnerException { + Options opt = + new OptionsBuilder() + .include(ReadBenchmark.class.getSimpleName()) + .result("jmh-result.json") + .resultFormat(ResultFormatType.JSON) + .build(); + new Runner(opt).run(); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsTest.java index e9af95d9de..18fd5cb614 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsTest.java @@ -436,6 +436,8 @@ public void testBuilderSetUri() { "cloudspanner://spanner.googleapis.com/projects/test-project-123/instances/test-instance?autocommit=true;readonly=false"); builder.setUri( "cloudspanner://spanner.googleapis.com/projects/test-project-123?autocommit=true;readonly=false"); + builder.setUri( + "cloudspanner://spanner.googleapis.com/projects/test-project-123?statement_timeout='10s';transaction_timeout='60s'"); // set invalid uri's setInvalidUri( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/GrpcInterceptorProviderTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/GrpcInterceptorProviderTest.java new file mode 100644 index 0000000000..0845d1d9c3 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/GrpcInterceptorProviderTest.java @@ -0,0 +1,117 @@ +/* + * Copyright 2025 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 static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import com.google.api.gax.grpc.GrpcInterceptorProvider; +import com.google.cloud.spanner.ErrorCode; +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.SpannerException; +import com.google.common.collect.ImmutableList; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.MethodDescriptor; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class GrpcInterceptorProviderTest extends AbstractMockServerTest { + private static final AtomicBoolean INTERCEPTOR_CALLED = new AtomicBoolean(false); + + public static final class TestGrpcInterceptorProvider implements GrpcInterceptorProvider { + @Override + public List getInterceptors() { + return ImmutableList.of( + new ClientInterceptor() { + @Override + public ClientCall interceptCall( + MethodDescriptor method, CallOptions callOptions, Channel next) { + INTERCEPTOR_CALLED.set(true); + return next.newCall(method, callOptions); + } + }); + } + } + + @Before + public void clearInterceptorUsedFlag() { + INTERCEPTOR_CALLED.set(false); + } + + @Test + public void testGrpcInterceptorProviderIsNotUsedByDefault() { + assertFalse(INTERCEPTOR_CALLED.get()); + try (Connection connection = createConnection()) { + try (ResultSet resultSet = connection.executeQuery(SELECT1_STATEMENT)) { + while (resultSet.next()) { + // ignore + } + } + } + assertFalse(INTERCEPTOR_CALLED.get()); + } + + @Test + public void testGrpcInterceptorProviderIsUsedWhenConfigured() { + System.setProperty("ENABLE_GRPC_INTERCEPTOR_PROVIDER", "true"); + assertFalse(INTERCEPTOR_CALLED.get()); + try (Connection connection = + createConnection( + ";grpc_interceptor_provider=" + TestGrpcInterceptorProvider.class.getName())) { + try (ResultSet resultSet = connection.executeQuery(SELECT1_STATEMENT)) { + while (resultSet.next()) { + // ignore + } + } + } finally { + System.clearProperty("ENABLE_GRPC_INTERCEPTOR_PROVIDER"); + } + assertTrue(INTERCEPTOR_CALLED.get()); + } + + @Test + public void testGrpcInterceptorProviderRequiresSystemProperty() { + assertFalse(INTERCEPTOR_CALLED.get()); + SpannerException exception = + assertThrows( + SpannerException.class, + () -> + createConnection( + ";grpc_interceptor_provider=" + TestGrpcInterceptorProvider.class.getName())); + assertEquals(ErrorCode.FAILED_PRECONDITION, exception.getErrorCode()); + assertTrue( + exception.getMessage(), + exception + .getMessage() + .contains( + "grpc_interceptor_provider can only be used if the system property" + + " ENABLE_GRPC_INTERCEPTOR_PROVIDER has been set to true. Start the" + + " application with the JVM command line option" + + " -DENABLE_GRPC_INTERCEPTOR_PROVIDER=true")); + assertFalse(INTERCEPTOR_CALLED.get()); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementTimeoutTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementTimeoutTest.java index b1adb3861b..e854b3d9d9 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementTimeoutTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementTimeoutTest.java @@ -108,10 +108,12 @@ public static Object[] parameters() { @Parameter public StatementExecutorType statementExecutorType; - protected ITConnection createConnection() { + protected ITConnection createConnection(String additionalUrlOptions) { + String urlSuffix = + ";trackSessionLeaks=false" + (additionalUrlOptions == null ? "" : additionalUrlOptions); ConnectionOptions options = ConnectionOptions.newBuilder() - .setUri(getBaseUrl() + ";trackSessionLeaks=false") + .setUri(getBaseUrl() + urlSuffix) .setStatementExecutorType(statementExecutorType) .setConfigurator( optionsConfigurator -> { @@ -135,6 +137,10 @@ protected ITConnection createConnection() { return createITConnection(options); } + protected ITConnection createConnection() { + return createConnection(""); + } + @Before public void setup() { // Set up a connection and get the dialect to ensure that the auto-detect-dialect query has @@ -169,6 +175,22 @@ public void testTimeoutExceptionReadOnlyAutocommit() { } } + @Test + public void testUrlTimeoutExceptionReadOnlyAutocommit() { + mockSpanner.setExecuteStreamingSqlExecutionTime( + SimulatedExecutionTime.ofMinimumAndRandomTime(EXECUTION_TIME_SLOW_STATEMENT, 0)); + + try (Connection connection = + createConnection(";statement_timeout='" + TIMEOUT_FOR_SLOW_STATEMENTS + "ms'")) { + connection.setAutocommit(true); + connection.setReadOnly(true); + SpannerException e = + assertThrows( + SpannerException.class, () -> connection.executeQuery(SELECT_RANDOM_STATEMENT)); + assertEquals(ErrorCode.DEADLINE_EXCEEDED, e.getErrorCode()); + } + } + @Test public void testTimeoutExceptionReadOnlyAutocommitMultipleStatements() { mockSpanner.setExecuteStreamingSqlExecutionTime( @@ -277,6 +299,30 @@ public void testTimeoutExceptionReadWriteAutocommitMultipleStatements() { } } + @Test + public void testUrlStatementTimeoutOverrideToSucceed() { + mockSpanner.setExecuteStreamingSqlExecutionTime( + SimulatedExecutionTime.ofMinimumAndRandomTime(EXECUTION_TIME_SLOW_STATEMENT, 0)); + + try (Connection connection = + createConnection(";statement_timeout='" + TIMEOUT_FOR_SLOW_STATEMENTS + "ms'")) { + connection.setAutocommit(true); + for (int i = 0; i < 2; i++) { + SpannerException e = + assertThrows( + SpannerException.class, () -> connection.executeQuery(SELECT_RANDOM_STATEMENT)); + assertEquals(ErrorCode.DEADLINE_EXCEEDED, e.getErrorCode()); + } + + // Remove slow behavior and verify a fast query succeeds after overriding the timeout. + mockSpanner.removeAllExecutionTimes(); + connection.setStatementTimeout(TIMEOUT_FOR_FAST_STATEMENTS, TimeUnit.MILLISECONDS); + try (ResultSet rs = connection.executeQuery(SELECT_RANDOM_STATEMENT)) { + assertNotNull(rs); + } + } + } + @Test public void testTimeoutExceptionReadWriteAutocommitSlowUpdate() { mockSpanner.setExecuteSqlExecutionTime( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java index dbefb73294..76b5436aba 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java @@ -56,6 +56,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Random; import java.util.Vector; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -74,6 +75,8 @@ public class ITTransactionTest { @ClassRule public static IntegrationTestEnv env = new IntegrationTestEnv(); private static Database db; private static DatabaseClient client; + private static Database largeMessageDb; + private static DatabaseClient largeMessageClient; /** Sequence for assigning unique keys to test cases. */ private static int seq; @@ -88,11 +91,31 @@ public static void setUpDatabase() { + " V INT64," + ") PRIMARY KEY (K)"); client = env.getTestHelper().getDatabaseClient(db); + + largeMessageDb = + env.getTestHelper() + .createTestDatabase( + "CREATE TABLE T (" + + " K STRING(MAX) NOT NULL," + + " col0 BYTES(MAX)," + + " col1 BYTES(MAX)," + + " col2 BYTES(MAX)," + + " col3 BYTES(MAX)," + + " col4 BYTES(MAX)," + + " col5 BYTES(MAX)," + + " col6 BYTES(MAX)," + + " col7 BYTES(MAX)," + + " col8 BYTES(MAX)," + + " col9 BYTES(MAX)," + + ") PRIMARY KEY (K)"); + largeMessageClient = env.getTestHelper().getDatabaseClient(largeMessageDb); } @Before public void removeTestData() { client.writeAtLeastOnce(Collections.singletonList(Mutation.delete("T", KeySet.all()))); + largeMessageClient.writeAtLeastOnce( + Collections.singletonList(Mutation.delete("T", KeySet.all()))); } private static String uniqueKey() { @@ -561,6 +584,25 @@ public void testTxWithUncaughtError() { } } + @Test + public void testTxWithLargeMessageSize() { + int bytesPerColumn = 10000000; // 10MB + String key = uniqueKey(); + Random random = new Random(); + List mutations = new ArrayList(); + Mutation.WriteBuilder builder = Mutation.newInsertOrUpdateBuilder("T").set("K").to(key); + for (int j = 0; j < 7; j++) { + byte[] data = new byte[bytesPerColumn]; + random.nextBytes(data); + builder + .set("col" + j) + .to(com.google.cloud.spanner.Value.bytes(com.google.cloud.ByteArray.copyFrom(data))); + } + mutations.add(builder.build()); + // This large message is under the 100MB limit. + largeMessageClient.write(mutations); + } + @Test public void testTxWithUncaughtErrorAfterSuccessfulBegin() { try { diff --git a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/jmh/jmh-baseline.json b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/jmh/jmh-baseline.json new file mode 100644 index 0000000000..7753f173ee --- /dev/null +++ b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/jmh/jmh-baseline.json @@ -0,0 +1,22 @@ +{ + "benchmarkResultMap": { + "com.google.cloud.spanner.benchmarking.ReadBenchmark.queryBenchmark": { + "scorePercentiles": [ + { + "percentile": "50.0", + "baseline": "450", + "difference": "15" + } + ] + }, + "com.google.cloud.spanner.benchmarking.ReadBenchmark.readBenchmark": { + "scorePercentiles": [ + { + "percentile": "50.0", + "baseline": "450", + "difference": "15" + } + ] + } + } +} \ No newline at end of file diff --git a/grpc-google-cloud-spanner-admin-database-v1/pom.xml b/grpc-google-cloud-spanner-admin-database-v1/pom.xml index a89656fcfc..649a7b4226 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.101.1 + 6.102.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.101.1 + 6.102.0 diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml index c5e56e818d..9d0e8cca71 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.101.1 + 6.102.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.101.1 + 6.102.0 diff --git a/grpc-google-cloud-spanner-executor-v1/pom.xml b/grpc-google-cloud-spanner-executor-v1/pom.xml index f143922638..bba5a5cd4b 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.101.1 + 6.102.0 grpc-google-cloud-spanner-executor-v1 GRPC library for google-cloud-spanner com.google.cloud google-cloud-spanner-parent - 6.101.1 + 6.102.0 diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml index 0e2812e71f..ebec943989 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.101.1 + 6.102.0 grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.101.1 + 6.102.0 diff --git a/pom.xml b/pom.xml index 3ed2cc20b5..275fad8258 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-spanner-parent pom - 6.101.1 + 6.102.0 Google Cloud Spanner Parent https://github.com/googleapis/java-spanner @@ -14,7 +14,7 @@ com.google.cloud sdk-platform-java-config - 3.52.2 + 3.52.3 @@ -61,47 +61,47 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.101.1 + 6.102.0 com.google.api.grpc proto-google-cloud-spanner-executor-v1 - 6.101.1 + 6.102.0 com.google.api.grpc grpc-google-cloud-spanner-executor-v1 - 6.101.1 + 6.102.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.101.1 + 6.102.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.101.1 + 6.102.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.101.1 + 6.102.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.101.1 + 6.102.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.101.1 + 6.102.0 com.google.cloud google-cloud-spanner - 6.101.1 + 6.102.0 @@ -121,7 +121,7 @@ com.google.truth truth - 1.4.4 + 1.4.5 test @@ -153,7 +153,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.14.0 + 3.14.1 1.8 1.8 diff --git a/proto-google-cloud-spanner-admin-database-v1/pom.xml b/proto-google-cloud-spanner-admin-database-v1/pom.xml index af692c6a4d..1d6d70f6ee 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.101.1 + 6.102.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.101.1 + 6.102.0 diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml index 2b129d2faf..66cba86dba 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.101.1 + 6.102.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.101.1 + 6.102.0 diff --git a/proto-google-cloud-spanner-executor-v1/pom.xml b/proto-google-cloud-spanner-executor-v1/pom.xml index 557343a2e3..82f9dc550a 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.101.1 + 6.102.0 proto-google-cloud-spanner-executor-v1 Proto library for google-cloud-spanner com.google.cloud google-cloud-spanner-parent - 6.101.1 + 6.102.0 diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml index 4acd5bf612..12e8c39d2e 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.101.1 + 6.102.0 proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.101.1 + 6.102.0 diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index e24840bbb8..a4efda2e16 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -23,8 +23,8 @@ 1.8 UTF-8 0.31.1 - 2.62.0 - 3.63.0 + 2.76.0 + 3.77.0 @@ -33,7 +33,7 @@ com.google.cloud google-cloud-spanner - 6.99.0 + 6.101.1 @@ -100,7 +100,7 @@ com.google.truth truth - 1.4.4 + 1.4.5 test @@ -116,7 +116,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.6.0 + 3.6.1 add-snippets-source @@ -145,7 +145,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.5.3 + 3.5.4 10 false diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index b8d534a4c8..f94f84eb0b 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -23,8 +23,8 @@ 1.8 UTF-8 0.31.1 - 2.62.0 - 3.63.0 + 2.76.0 + 3.77.0 @@ -32,7 +32,7 @@ com.google.cloud google-cloud-spanner - 6.101.1 + 6.102.0 @@ -99,7 +99,7 @@ com.google.truth truth - 1.4.4 + 1.4.5 test @@ -115,7 +115,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.6.0 + 3.6.1 add-snippets-source @@ -144,7 +144,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.5.3 + 3.5.4 10 false diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 25be82edd8..5b2164f0cb 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -34,7 +34,7 @@ com.google.cloud libraries-bom - 26.68.0 + 26.69.0 pom import @@ -111,7 +111,7 @@ com.google.truth truth - 1.4.4 + 1.4.5 test @@ -126,7 +126,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.5.3 + 3.5.4 10 false @@ -155,7 +155,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.5.3 + 3.5.4 10 false diff --git a/versions.txt b/versions.txt index ee56d9a157..2ba0e25302 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.101.1:6.101.1 -proto-google-cloud-spanner-v1:6.101.1:6.101.1 -proto-google-cloud-spanner-admin-database-v1:6.101.1:6.101.1 -grpc-google-cloud-spanner-v1:6.101.1:6.101.1 -grpc-google-cloud-spanner-admin-instance-v1:6.101.1:6.101.1 -grpc-google-cloud-spanner-admin-database-v1:6.101.1:6.101.1 -google-cloud-spanner:6.101.1:6.101.1 -google-cloud-spanner-executor:6.101.1:6.101.1 -proto-google-cloud-spanner-executor-v1:6.101.1:6.101.1 -grpc-google-cloud-spanner-executor-v1:6.101.1:6.101.1 +proto-google-cloud-spanner-admin-instance-v1:6.102.0:6.102.0 +proto-google-cloud-spanner-v1:6.102.0:6.102.0 +proto-google-cloud-spanner-admin-database-v1:6.102.0:6.102.0 +grpc-google-cloud-spanner-v1:6.102.0:6.102.0 +grpc-google-cloud-spanner-admin-instance-v1:6.102.0:6.102.0 +grpc-google-cloud-spanner-admin-database-v1:6.102.0:6.102.0 +google-cloud-spanner:6.102.0:6.102.0 +google-cloud-spanner-executor:6.102.0:6.102.0 +proto-google-cloud-spanner-executor-v1:6.102.0:6.102.0 +grpc-google-cloud-spanner-executor-v1:6.102.0:6.102.0