From b34a742e9ede635ecf0777e150c41bd208113bb1 Mon Sep 17 00:00:00 2001 From: Sri Harsha CH Date: Thu, 30 May 2024 10:31:10 +0000 Subject: [PATCH 1/8] feat(spanner): environment variable to enable multiplexed sessions --- .github/workflows/ci.yaml | 4 +- ...nst-emulator-with-multiplexed-session.yaml | 2 +- ...tegration-multiplexed-sessions-enabled.cfg | 4 +- .../cloud/spanner/SessionPoolOptions.java | 35 ++++----- .../MultiplexedSessionDatabaseClientTest.java | 73 +++++++++++++++++-- 5 files changed, 89 insertions(+), 29 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0e38c416770..7eca4c6d5f0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -52,7 +52,7 @@ jobs: - run: .kokoro/build.sh env: JOB_TYPE: test - GOOGLE_CLOUD_SPANNER_ENABLE_MULTIPLEXED_SESSIONS: true + GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS: true units-java8: # Building using Java 17 and run the tests with Java 8 runtime name: "units (8)" @@ -91,7 +91,7 @@ jobs: - run: .kokoro/build.sh env: JOB_TYPE: test - GOOGLE_CLOUD_SPANNER_ENABLE_MULTIPLEXED_SESSIONS: true + GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS: true windows: runs-on: windows-latest steps: diff --git a/.github/workflows/integration-tests-against-emulator-with-multiplexed-session.yaml b/.github/workflows/integration-tests-against-emulator-with-multiplexed-session.yaml index 741fecb089d..bd7dfef3972 100644 --- a/.github/workflows/integration-tests-against-emulator-with-multiplexed-session.yaml +++ b/.github/workflows/integration-tests-against-emulator-with-multiplexed-session.yaml @@ -39,4 +39,4 @@ jobs: env: JOB_TYPE: test SPANNER_EMULATOR_HOST: localhost:9010 - GOOGLE_CLOUD_SPANNER_ENABLE_MULTIPLEXED_SESSIONS: true + GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS: true diff --git a/.kokoro/presubmit/integration-multiplexed-sessions-enabled.cfg b/.kokoro/presubmit/integration-multiplexed-sessions-enabled.cfg index 0acb1a445b0..771405de422 100644 --- a/.kokoro/presubmit/integration-multiplexed-sessions-enabled.cfg +++ b/.kokoro/presubmit/integration-multiplexed-sessions-enabled.cfg @@ -33,6 +33,6 @@ env_vars: { } env_vars: { - key: "GOOGLE_CLOUD_SPANNER_ENABLE_MULTIPLEXED_SESSIONS" + key: "GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS" value: "true" -} \ No newline at end of file +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java index 382bef1b5a2..4b97411bf74 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java @@ -105,11 +105,12 @@ private SessionPoolOptions(Builder builder) { this.randomizePositionQPSThreshold = builder.randomizePositionQPSThreshold; this.inactiveTransactionRemovalOptions = builder.inactiveTransactionRemovalOptions; this.poolMaintainerClock = builder.poolMaintainerClock; - // TODO: Remove when multiplexed sessions are guaranteed to be supported. + // useMultiplexedSession priority => Environment var > private setter > client default + Boolean useMultiplexedSessionFromEnvVariable = getUseMultiplexedSessionFromEnvVariable(); this.useMultiplexedSession = - builder.useMultiplexedSession - && !Boolean.parseBoolean( - System.getenv("GOOGLE_CLOUD_SPANNER_FORCE_DISABLE_MULTIPLEXED_SESSIONS")); + (useMultiplexedSessionFromEnvVariable != null) + ? useMultiplexedSessionFromEnvVariable + : builder.useMultiplexedSession; this.useRandomChannelHint = builder.useRandomChannelHint; this.multiplexedSessionMaintenanceDuration = builder.multiplexedSessionMaintenanceDuration; } @@ -312,6 +313,16 @@ public boolean getUseMultiplexedSession() { return useMultiplexedSession; } + private static Boolean getUseMultiplexedSessionFromEnvVariable() { + String useMultiplexedSessionFromEnvVariable = + System.getenv("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS"); + if (useMultiplexedSessionFromEnvVariable != null + && useMultiplexedSessionFromEnvVariable.length() > 0) { + return Boolean.parseBoolean(useMultiplexedSessionFromEnvVariable); + } + return null; + } + boolean isUseRandomChannelHint() { return useRandomChannelHint; } @@ -518,7 +529,9 @@ public static class Builder { */ private long randomizePositionQPSThreshold = 0L; - private boolean useMultiplexedSession = getUseMultiplexedSessionFromEnvVariable(); + // This field controls the default behavior of session management in Java client. + // Set useMultiplexedSession to true to make multiplexed session as default. + private boolean useMultiplexedSession = false; private boolean useRandomChannelHint; @@ -538,18 +551,6 @@ private static Position getReleaseToPositionFromSystemProperty() { return Position.FIRST; } - /** - * This environment is only added to support internal spanner testing. Support for it can be - * removed in the future. Use {@link SessionPoolOptions#useMultiplexedSession} instead to use - * multiplexed sessions. - */ - @InternalApi - @BetaApi - private static boolean getUseMultiplexedSessionFromEnvVariable() { - return Boolean.parseBoolean( - System.getenv("GOOGLE_CLOUD_SPANNER_ENABLE_MULTIPLEXED_SESSIONS")); - } - public Builder() {} private Builder(SessionPoolOptions options) { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientTest.java index d7d9b7395ed..a64c615b4e9 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientTest.java @@ -110,10 +110,9 @@ public void testMaintainer() { } @Test - public void testForceDisableEnvVar() throws Exception { + public void testDisableMultiplexedSessionEnvVar() throws Exception { assumeTrue(isJava8() && !isWindows()); - assumeFalse( - System.getenv().containsKey("GOOGLE_CLOUD_SPANNER_FORCE_DISABLE_MULTIPLEXED_SESSIONS")); + assumeFalse(System.getenv().containsKey("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS")); // Assert that the mux sessions setting is respected by default. assertTrue( @@ -129,8 +128,7 @@ public void testForceDisableEnvVar() throws Exception { (Map) field.get(System.getenv()); try { - writeableEnvironmentVariables.put( - "GOOGLE_CLOUD_SPANNER_FORCE_DISABLE_MULTIPLEXED_SESSIONS", "true"); + writeableEnvironmentVariables.put("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS", "false"); // Assert that the env var overrides the mux sessions setting. assertFalse( SessionPoolOptions.newBuilder() @@ -138,8 +136,69 @@ public void testForceDisableEnvVar() throws Exception { .build() .getUseMultiplexedSession()); } finally { - writeableEnvironmentVariables.remove( - "GOOGLE_CLOUD_SPANNER_FORCE_DISABLE_MULTIPLEXED_SESSIONS"); + writeableEnvironmentVariables.remove("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS"); + } + } + + @Test + public void testEnableMultiplexedSessionEnvVar() throws Exception { + assumeTrue(isJava8() && !isWindows()); + assumeFalse(System.getenv().containsKey("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS")); + + // Assert that the mux sessions setting is respected by default. + assertFalse( + SessionPoolOptions.newBuilder() + .setUseMultiplexedSession(false) + .build() + .getUseMultiplexedSession()); + + Class classOfMap = System.getenv().getClass(); + Field field = classOfMap.getDeclaredField("m"); + field.setAccessible(true); + Map writeableEnvironmentVariables = + (Map) field.get(System.getenv()); + + try { + writeableEnvironmentVariables.put("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS", "true"); + // Assert that the env var overrides the mux sessions setting. + assertTrue( + SessionPoolOptions.newBuilder() + .setUseMultiplexedSession(false) + .build() + .getUseMultiplexedSession()); + } finally { + writeableEnvironmentVariables.remove("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS"); + } + } + + @Test + public void testIgnoreMultiplexedSessionEnvVar() throws Exception { + assumeTrue(isJava8() && !isWindows()); + assumeFalse(System.getenv().containsKey("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS")); + + // Assert that the mux sessions setting is respected by default. + assertFalse( + SessionPoolOptions.newBuilder() + .setUseMultiplexedSession(false) + .build() + .getUseMultiplexedSession()); + + Class classOfMap = System.getenv().getClass(); + Field field = classOfMap.getDeclaredField("m"); + field.setAccessible(true); + Map writeableEnvironmentVariables = + (Map) field.get(System.getenv()); + + try { + writeableEnvironmentVariables.put("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS", ""); + // Assert that the env var overrides the mux sessions setting. + assertFalse( + SessionPoolOptions.newBuilder() + .setUseMultiplexedSession(false) + .build() + .getUseMultiplexedSession()); + } finally { + writeableEnvironmentVariables.remove("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS"); } } From bca5829f7867763f27c85aa0036f5b23f7dc68af Mon Sep 17 00:00:00 2001 From: Sri Harsha CH Date: Thu, 30 May 2024 10:53:09 +0000 Subject: [PATCH 2/8] chore: lint fix --- .../main/java/com/google/cloud/spanner/SessionPoolOptions.java | 1 - 1 file changed, 1 deletion(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java index 4b97411bf74..55e55dc95ec 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java @@ -16,7 +16,6 @@ package com.google.cloud.spanner; -import com.google.api.core.BetaApi; import com.google.api.core.InternalApi; import com.google.cloud.spanner.SessionPool.Position; import com.google.common.annotations.VisibleForTesting; From 9704c27470fdd5095ef53b7a9f2a39e3e1e9d56f Mon Sep 17 00:00:00 2001 From: Sri Harsha CH Date: Mon, 24 Jun 2024 11:51:10 +0000 Subject: [PATCH 3/8] feat(spanner): add comment for gRPC-GCP --- .../java/com/google/cloud/spanner/SpannerOptions.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 639943d9970..69c548f3ab4 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 @@ -1266,14 +1266,20 @@ public Builder setHost(String host) { return this; } - /** Enables gRPC-GCP extension with the default settings. */ + /** + * Enables gRPC-GCP extension with the default settings. Do not set + * GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS to true, since Multiplexed sessions is not + * supported for gRPC-GCP. + */ public Builder enableGrpcGcpExtension() { return this.enableGrpcGcpExtension(null); } /** * Enables gRPC-GCP extension and uses provided options for configuration. The metric registry - * and default Spanner metric labels will be added automatically. + * and default Spanner metric labels will be added automatically. Do not set + * GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS to true, since Multiplexed sessions is not + * supported for gRPC-GCP. */ public Builder enableGrpcGcpExtension(GcpManagedChannelOptions options) { this.grpcGcpExtensionEnabled = true; From e647f4f6cf59d6ce72f74495cbf06bb01bdb8e03 Mon Sep 17 00:00:00 2001 From: Sri Harsha CH Date: Mon, 24 Jun 2024 15:49:03 +0000 Subject: [PATCH 4/8] feat(spanner): make strict check in env variable --- .../com/google/cloud/spanner/SessionPoolOptions.java | 10 ++++++++-- .../java/com/google/cloud/spanner/SpannerOptions.java | 8 ++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java index 55e55dc95ec..de81a0e7f33 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java @@ -317,7 +317,13 @@ private static Boolean getUseMultiplexedSessionFromEnvVariable() { System.getenv("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS"); if (useMultiplexedSessionFromEnvVariable != null && useMultiplexedSessionFromEnvVariable.length() > 0) { - return Boolean.parseBoolean(useMultiplexedSessionFromEnvVariable); + if ("true".equalsIgnoreCase(useMultiplexedSessionFromEnvVariable) + || "false".equalsIgnoreCase(useMultiplexedSessionFromEnvVariable)) { + return Boolean.parseBoolean(useMultiplexedSessionFromEnvVariable); + } else { + throw new IllegalArgumentException( + "GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS should be either true or false."); + } } return null; } @@ -529,7 +535,7 @@ public static class Builder { private long randomizePositionQPSThreshold = 0L; // This field controls the default behavior of session management in Java client. - // Set useMultiplexedSession to true to make multiplexed session as default. + // Set useMultiplexedSession to true to make multiplexed session the default. private boolean useMultiplexedSession = false; private boolean useRandomChannelHint; 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 69c548f3ab4..6ddfeac85c7 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 @@ -1268,8 +1268,8 @@ public Builder setHost(String host) { /** * Enables gRPC-GCP extension with the default settings. Do not set - * GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS to true, since Multiplexed sessions is not - * supported for gRPC-GCP. + * GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS to true in combination with this option, as + * Multiplexed sessions are not supported for gRPC-GCP. */ public Builder enableGrpcGcpExtension() { return this.enableGrpcGcpExtension(null); @@ -1278,8 +1278,8 @@ public Builder enableGrpcGcpExtension() { /** * Enables gRPC-GCP extension and uses provided options for configuration. The metric registry * and default Spanner metric labels will be added automatically. Do not set - * GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS to true, since Multiplexed sessions is not - * supported for gRPC-GCP. + * GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS to true in combination with this option, as + * Multiplexed sessions are not supported for gRPC-GCP. */ public Builder enableGrpcGcpExtension(GcpManagedChannelOptions options) { this.grpcGcpExtensionEnabled = true; From 71c0f6224fc32a87a944f6da39c0c5f33dde929c Mon Sep 17 00:00:00 2001 From: Sri Harsha CH Date: Tue, 25 Jun 2024 10:33:56 +0000 Subject: [PATCH 5/8] feat(spanner): add test --- .../MultiplexedSessionDatabaseClientTest.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientTest.java index a64c615b4e9..3c24ce78ca4 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientTest.java @@ -16,8 +16,10 @@ package com.google.cloud.spanner; +import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; @@ -27,6 +29,8 @@ import static org.mockito.Mockito.when; import com.google.cloud.spanner.SessionClient.SessionConsumer; +import java.io.PrintWriter; +import java.io.StringWriter; import java.lang.reflect.Field; import java.time.Clock; import java.time.Duration; @@ -202,6 +206,45 @@ public void testIgnoreMultiplexedSessionEnvVar() throws Exception { } } + @Test + public void testThrowExceptionMultiplexedSessionEnvVarInvalidValues() throws Exception { + // assumeTrue(isJava8() && !isWindows()); + assumeFalse(System.getenv().containsKey("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS")); + + // Assert that the mux sessions setting is respected by default. + assertFalse( + SessionPoolOptions.newBuilder() + .setUseMultiplexedSession(false) + .build() + .getUseMultiplexedSession()); + + Class classOfMap = System.getenv().getClass(); + Field field = classOfMap.getDeclaredField("m"); + field.setAccessible(true); + Map writeableEnvironmentVariables = + (Map) field.get(System.getenv()); + + try { + writeableEnvironmentVariables.put("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS", "test"); + + // setting an invalid GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS value throws error. + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> + SessionPoolOptions.newBuilder() + .setUseMultiplexedSession(false) + .build() + .getUseMultiplexedSession()); + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + assertThat(sw.toString()) + .contains("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS should be either true or false"); + } finally { + writeableEnvironmentVariables.remove("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS"); + } + } + private boolean isJava8() { return JavaVersionUtil.getJavaMajorVersion() == 8; } From ad1ee20d526f4ccf34c403d3fcc5aeef29d341bf Mon Sep 17 00:00:00 2001 From: Sri Harsha CH Date: Tue, 25 Jun 2024 10:41:15 +0000 Subject: [PATCH 6/8] feat(spanner): add test --- .../cloud/spanner/MultiplexedSessionDatabaseClientTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientTest.java index 3c24ce78ca4..287fdd0bd0b 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientTest.java @@ -208,7 +208,7 @@ public void testIgnoreMultiplexedSessionEnvVar() throws Exception { @Test public void testThrowExceptionMultiplexedSessionEnvVarInvalidValues() throws Exception { - // assumeTrue(isJava8() && !isWindows()); + assumeTrue(isJava8() && !isWindows()); assumeFalse(System.getenv().containsKey("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS")); // Assert that the mux sessions setting is respected by default. From 1ed9581e09030f077f916298c7e7e062a85ecdbb Mon Sep 17 00:00:00 2001 From: Sri Harsha CH Date: Tue, 2 Jul 2024 10:29:21 +0000 Subject: [PATCH 7/8] chore(spanner): lint --- .../main/java/com/google/cloud/spanner/SessionPoolOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java index 1981a4e033c..2a065c8b2ce 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java @@ -322,7 +322,7 @@ private static Boolean getUseMultiplexedSessionFromEnvVariable() { } return null; } - + Duration getMultiplexedSessionMaintenanceDuration() { return multiplexedSessionMaintenanceDuration; } From 8bc75c5dcf0a6647e4677582f64bec0d5e5d5379 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Tue, 2 Jul 2024 10:48:55 +0000 Subject: [PATCH 8/8] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20po?= =?UTF-8?q?st-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 81c0d3bdbbc..c1185874ce3 100644 --- a/README.md +++ b/README.md @@ -57,13 +57,13 @@ 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.69.0' +implementation 'com.google.cloud:google-cloud-spanner:6.70.0' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.69.0" +libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.70.0" ``` @@ -687,7 +687,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.69.0 +[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.70.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