Skip to content
This repository was archived by the owner on Apr 7, 2026. It is now read-only.

Commit 600a32d

Browse files
chore: merge main into generate-libraries-main
2 parents 032e78f + 1cbc00b commit 600a32d

43 files changed

Lines changed: 359 additions & 128 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
# Changelog
22

3+
## [6.96.0](https://github.com/googleapis/java-spanner/compare/v6.95.1...v6.96.0) (2025-06-27)
4+
5+
6+
### Features
7+
8+
* Allow JDBC to configure directpath for connection ([#3929](https://github.com/googleapis/java-spanner/issues/3929)) ([d754f1f](https://github.com/googleapis/java-spanner/commit/d754f1f99294d86ec881583f217fa09f291a3d7a))
9+
* Support getOrNull and getOrDefault in Struct ([#3914](https://github.com/googleapis/java-spanner/issues/3914)) ([1dc5a3e](https://github.com/googleapis/java-spanner/commit/1dc5a3ec0ca9ea530e8691df5c2734c0a1ece559))
10+
* Use multiplexed sessions for read-only transactions ([#3917](https://github.com/googleapis/java-spanner/issues/3917)) ([37fdc27](https://github.com/googleapis/java-spanner/commit/37fdc27aab4e71ac141c2a2c979f864e97395a97))
11+
12+
13+
### Bug Fixes
14+
15+
* Allow zero durations to be set for connections ([#3916](https://github.com/googleapis/java-spanner/issues/3916)) ([43ea4fa](https://github.com/googleapis/java-spanner/commit/43ea4fa68eac00801beb8e58c1eb09e9f32e5ce5))
16+
17+
18+
### Documentation
19+
20+
* Add snippet for Repeatable Read configuration at client and transaction ([#3908](https://github.com/googleapis/java-spanner/issues/3908)) ([ff3d212](https://github.com/googleapis/java-spanner/commit/ff3d212c98276c4084f44619916d0444c9652803))
21+
* Update SpannerSample.java to align with best practices ([#3625](https://github.com/googleapis/java-spanner/issues/3625)) ([7bfc62d](https://github.com/googleapis/java-spanner/commit/7bfc62d3d9e57242e0dfddea090208f8c65f0f8e))
22+
323
## [6.95.1](https://github.com/googleapis/java-spanner/compare/v6.95.0...v6.95.1) (2025-06-06)
424

525

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,13 @@ implementation 'com.google.cloud:google-cloud-spanner'
5656
If you are using Gradle without BOM, add this to your dependencies:
5757

5858
```Groovy
59-
implementation 'com.google.cloud:google-cloud-spanner:6.95.1'
59+
implementation 'com.google.cloud:google-cloud-spanner:6.96.0'
6060
```
6161

6262
If you are using SBT, add this to your dependencies:
6363

6464
```Scala
65-
libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.95.1"
65+
libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.96.0"
6666
```
6767

6868
## Authentication
@@ -731,7 +731,7 @@ Java is a registered trademark of Oracle and/or its affiliates.
731731
[kokoro-badge-link-5]: http://storage.googleapis.com/cloud-devrel-public/java/badges/java-spanner/java11.html
732732
[stability-image]: https://img.shields.io/badge/stability-stable-green
733733
[maven-version-image]: https://img.shields.io/maven-central/v/com.google.cloud/google-cloud-spanner.svg
734-
[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.95.1
734+
[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.96.0
735735
[authentication]: https://github.com/googleapis/google-cloud-java#authentication
736736
[auth-scopes]: https://developers.google.com/identity/protocols/oauth2/scopes
737737
[predefined-iam-roles]: https://cloud.google.com/iam/docs/understanding-roles#predefined_roles

benchmarks/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<parent>
2525
<groupId>com.google.cloud</groupId>
2626
<artifactId>google-cloud-spanner-parent</artifactId>
27-
<version>6.95.2-SNAPSHOT</version><!-- {x-version-update:google-cloud-spanner:current} -->
27+
<version>6.96.0</version><!-- {x-version-update:google-cloud-spanner:current} -->
2828
</parent>
2929

3030
<properties>

google-cloud-spanner-bom/pom.xml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<modelVersion>4.0.0</modelVersion>
44
<groupId>com.google.cloud</groupId>
55
<artifactId>google-cloud-spanner-bom</artifactId>
6-
<version>6.95.2-SNAPSHOT</version><!-- {x-version-update:google-cloud-spanner:current} -->
6+
<version>6.96.0</version><!-- {x-version-update:google-cloud-spanner:current} -->
77
<packaging>pom</packaging>
88
<parent>
99
<groupId>com.google.cloud</groupId>
@@ -53,43 +53,43 @@
5353
<dependency>
5454
<groupId>com.google.cloud</groupId>
5555
<artifactId>google-cloud-spanner</artifactId>
56-
<version>6.95.2-SNAPSHOT</version><!-- {x-version-update:google-cloud-spanner:current} -->
56+
<version>6.96.0</version><!-- {x-version-update:google-cloud-spanner:current} -->
5757
</dependency>
5858
<dependency>
5959
<groupId>com.google.cloud</groupId>
6060
<artifactId>google-cloud-spanner</artifactId>
6161
<type>test-jar</type>
62-
<version>6.95.2-SNAPSHOT</version><!-- {x-version-update:google-cloud-spanner:current} -->
62+
<version>6.96.0</version><!-- {x-version-update:google-cloud-spanner:current} -->
6363
</dependency>
6464
<dependency>
6565
<groupId>com.google.api.grpc</groupId>
6666
<artifactId>grpc-google-cloud-spanner-v1</artifactId>
67-
<version>6.95.2-SNAPSHOT</version><!-- {x-version-update:grpc-google-cloud-spanner-v1:current} -->
67+
<version>6.96.0</version><!-- {x-version-update:grpc-google-cloud-spanner-v1:current} -->
6868
</dependency>
6969
<dependency>
7070
<groupId>com.google.api.grpc</groupId>
7171
<artifactId>grpc-google-cloud-spanner-admin-instance-v1</artifactId>
72-
<version>6.95.2-SNAPSHOT</version><!-- {x-version-update:grpc-google-cloud-spanner-admin-instance-v1:current} -->
72+
<version>6.96.0</version><!-- {x-version-update:grpc-google-cloud-spanner-admin-instance-v1:current} -->
7373
</dependency>
7474
<dependency>
7575
<groupId>com.google.api.grpc</groupId>
7676
<artifactId>grpc-google-cloud-spanner-admin-database-v1</artifactId>
77-
<version>6.95.2-SNAPSHOT</version><!-- {x-version-update:grpc-google-cloud-spanner-admin-database-v1:current} -->
77+
<version>6.96.0</version><!-- {x-version-update:grpc-google-cloud-spanner-admin-database-v1:current} -->
7878
</dependency>
7979
<dependency>
8080
<groupId>com.google.api.grpc</groupId>
8181
<artifactId>proto-google-cloud-spanner-admin-instance-v1</artifactId>
82-
<version>6.95.2-SNAPSHOT</version><!-- {x-version-update:proto-google-cloud-spanner-admin-instance-v1:current} -->
82+
<version>6.96.0</version><!-- {x-version-update:proto-google-cloud-spanner-admin-instance-v1:current} -->
8383
</dependency>
8484
<dependency>
8585
<groupId>com.google.api.grpc</groupId>
8686
<artifactId>proto-google-cloud-spanner-v1</artifactId>
87-
<version>6.95.2-SNAPSHOT</version><!-- {x-version-update:proto-google-cloud-spanner-v1:current} -->
87+
<version>6.96.0</version><!-- {x-version-update:proto-google-cloud-spanner-v1:current} -->
8888
</dependency>
8989
<dependency>
9090
<groupId>com.google.api.grpc</groupId>
9191
<artifactId>proto-google-cloud-spanner-admin-database-v1</artifactId>
92-
<version>6.95.2-SNAPSHOT</version><!-- {x-version-update:proto-google-cloud-spanner-admin-database-v1:current} -->
92+
<version>6.96.0</version><!-- {x-version-update:proto-google-cloud-spanner-admin-database-v1:current} -->
9393
</dependency>
9494
</dependencies>
9595
</dependencyManagement>

google-cloud-spanner-executor/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
<modelVersion>4.0.0</modelVersion>
66
<groupId>com.google.cloud</groupId>
77
<artifactId>google-cloud-spanner-executor</artifactId>
8-
<version>6.95.2-SNAPSHOT</version><!-- {x-version-update:google-cloud-spanner-executor:current} -->
8+
<version>6.96.0</version><!-- {x-version-update:google-cloud-spanner-executor:current} -->
99
<packaging>jar</packaging>
1010
<name>Google Cloud Spanner Executor</name>
1111

1212
<parent>
1313
<groupId>com.google.cloud</groupId>
1414
<artifactId>google-cloud-spanner-parent</artifactId>
15-
<version>6.95.2-SNAPSHOT</version><!-- {x-version-update:google-cloud-spanner:current} -->
15+
<version>6.96.0</version><!-- {x-version-update:google-cloud-spanner:current} -->
1616
</parent>
1717

1818
<properties>

google-cloud-spanner/clirr-ignored-differences.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,4 +1028,9 @@
10281028
<className>com/google/cloud/spanner/StructReader</className>
10291029
<method>java.lang.Object getOrDefault(java.lang.String, java.util.function.BiFunction, java.lang.Object)</method>
10301030
</difference>
1031+
<difference>
1032+
<differenceType>7012</differenceType>
1033+
<className>com/google/cloud/spanner/SpannerOptions$SpannerEnvironment</className>
1034+
<method>boolean isEnableDirectAccess()</method>
1035+
</difference>
10311036
</differences>

google-cloud-spanner/pom.xml

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
<modelVersion>4.0.0</modelVersion>
44
<groupId>com.google.cloud</groupId>
55
<artifactId>google-cloud-spanner</artifactId>
6-
<version>6.95.2-SNAPSHOT</version><!-- {x-version-update:google-cloud-spanner:current} -->
6+
<version>6.96.0</version><!-- {x-version-update:google-cloud-spanner:current} -->
77
<packaging>jar</packaging>
88
<name>Google Cloud Spanner</name>
99
<url>https://github.com/googleapis/java-spanner</url>
1010
<description>Java idiomatic client for Google Cloud Spanner.</description>
1111
<parent>
1212
<groupId>com.google.cloud</groupId>
1313
<artifactId>google-cloud-spanner-parent</artifactId>
14-
<version>6.95.2-SNAPSHOT</version><!-- {x-version-update:google-cloud-spanner:current} -->
14+
<version>6.96.0</version><!-- {x-version-update:google-cloud-spanner:current} -->
1515
</parent>
1616
<properties>
1717
<site.installationModule>google-cloud-spanner</site.installationModule>
@@ -355,13 +355,6 @@
355355
<artifactId>grpc-rls</artifactId>
356356
<scope>runtime</scope>
357357
</dependency>
358-
359-
<dependency>
360-
<groupId>org.graalvm.sdk</groupId>
361-
<artifactId>graal-sdk</artifactId>
362-
<version>${graal-sdk.version}</version>
363-
<scope>provided</scope>
364-
</dependency>
365358
<dependency>
366359
<groupId>org.graalvm.sdk</groupId>
367360
<artifactId>nativeimage</artifactId>
@@ -592,7 +585,7 @@
592585
<spanner.testenv.config.class>com.google.cloud.spanner.GceTestEnvConfig</spanner.testenv.config.class>
593586
<spanner.testenv.instance>projects/directpath-prod-manual-testing/instances/spanner-testing</spanner.testenv.instance>
594587
<spanner.gce.config.project_id>directpath-prod-manual-testing</spanner.gce.config.project_id>
595-
<spanner.attempt_directpath>true</spanner.attempt_directpath>
588+
<spanner.enable_direct_access>true</spanner.enable_direct_access>
596589
<spanner.directpath_test_scenario>ipv4</spanner.directpath_test_scenario>
597590
</systemPropertyVariables>
598591
<forkedProcessTimeoutInSeconds>3000</forkedProcessTimeoutInSeconds>

google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -457,22 +457,30 @@ void initTransaction() {
457457
}
458458

459459
private void initTransactionInternal(BeginTransactionRequest request) {
460+
XGoogSpannerRequestId reqId =
461+
session.getRequestIdCreator().nextRequestId(session.getChannel(), 1);
460462
try {
461463
Transaction transaction =
462-
rpc.beginTransaction(request, getTransactionChannelHint(), isRouteToLeader());
464+
rpc.beginTransaction(
465+
request, reqId.withOptions(getTransactionChannelHint()), isRouteToLeader());
463466
if (!transaction.hasReadTimestamp()) {
464467
throw SpannerExceptionFactory.newSpannerException(
465-
ErrorCode.INTERNAL, "Missing expected transaction.read_timestamp metadata field");
468+
ErrorCode.INTERNAL,
469+
"Missing expected transaction.read_timestamp metadata field",
470+
reqId);
466471
}
467472
if (transaction.getId().isEmpty()) {
468473
throw SpannerExceptionFactory.newSpannerException(
469-
ErrorCode.INTERNAL, "Missing expected transaction.id metadata field");
474+
ErrorCode.INTERNAL, "Missing expected transaction.id metadata field", reqId);
470475
}
471476
try {
472477
timestamp = Timestamp.fromProto(transaction.getReadTimestamp());
473478
} catch (IllegalArgumentException e) {
474479
throw SpannerExceptionFactory.newSpannerException(
475-
ErrorCode.INTERNAL, "Bad value in transaction.read_timestamp metadata field", e);
480+
ErrorCode.INTERNAL,
481+
"Bad value in transaction.read_timestamp metadata field",
482+
e,
483+
reqId);
476484
}
477485
transactionId = transaction.getId();
478486
span.addAnnotation(
@@ -803,7 +811,8 @@ ResultSet executeQueryInternalWithOptions(
803811
tracer.createStatementAttributes(statement, options),
804812
session.getErrorHandler(),
805813
rpc.getExecuteQueryRetrySettings(),
806-
rpc.getExecuteQueryRetryableCodes()) {
814+
rpc.getExecuteQueryRetryableCodes(),
815+
session.getRequestIdCreator()) {
807816
@Override
808817
CloseableIterator<PartialResultSet> startStream(
809818
@Nullable ByteString resumeToken,
@@ -826,11 +835,12 @@ CloseableIterator<PartialResultSet> startStream(
826835
if (selector != null) {
827836
request.setTransaction(selector);
828837
}
838+
this.ensureNonNullXGoogRequestId();
829839
SpannerRpc.StreamingCall call =
830840
rpc.executeQuery(
831841
request.build(),
832842
stream.consumer(),
833-
getTransactionChannelHint(),
843+
this.xGoogRequestId.withOptions(getTransactionChannelHint()),
834844
isRouteToLeader());
835845
session.markUsed(clock.instant());
836846
stream.setCall(call, request.getTransaction().hasBegin());
@@ -1008,7 +1018,8 @@ ResultSet readInternalWithOptions(
10081018
tracer.createTableAttributes(table, readOptions),
10091019
session.getErrorHandler(),
10101020
rpc.getReadRetrySettings(),
1011-
rpc.getReadRetryableCodes()) {
1021+
rpc.getReadRetryableCodes(),
1022+
session.getRequestIdCreator()) {
10121023
@Override
10131024
CloseableIterator<PartialResultSet> startStream(
10141025
@Nullable ByteString resumeToken,
@@ -1029,11 +1040,13 @@ CloseableIterator<PartialResultSet> startStream(
10291040
builder.setTransaction(selector);
10301041
}
10311042
builder.setRequestOptions(buildRequestOptions(readOptions));
1043+
this.incrementXGoogRequestIdAttempt();
1044+
this.xGoogRequestId.setChannelId(session.getChannel());
10321045
SpannerRpc.StreamingCall call =
10331046
rpc.read(
10341047
builder.build(),
10351048
stream.consumer(),
1036-
getTransactionChannelHint(),
1049+
this.xGoogRequestId.withOptions(getTransactionChannelHint()),
10371050
isRouteToLeader());
10381051
session.markUsed(clock.instant());
10391052
stream.setCall(call, /* withBeginTransaction= */ builder.getTransaction().hasBegin());

google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClientImpl.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,11 @@ private List<Partition> partitionReadUsingIndex(
250250
}
251251
builder.setPartitionOptions(pbuilder.build());
252252

253+
XGoogSpannerRequestId reqId =
254+
session.getRequestIdCreator().nextRequestId(session.getChannel(), 1);
253255
final PartitionReadRequest request = builder.build();
254256
try {
255-
PartitionResponse response = rpc.partitionRead(request, options);
257+
PartitionResponse response = rpc.partitionRead(request, reqId.withOptions(options));
256258
ImmutableList.Builder<Partition> partitions = ImmutableList.builder();
257259
for (com.google.spanner.v1.Partition p : response.getPartitionsList()) {
258260
Partition partition =
@@ -272,6 +274,7 @@ private List<Partition> partitionReadUsingIndex(
272274
return partitionReadUsingIndex(
273275
partitionOptions, table, index, keys, columns, true, option);
274276
}
277+
e.setRequestId(reqId);
275278
throw e;
276279
}
277280
}
@@ -313,9 +316,11 @@ private List<Partition> partitionQuery(
313316
}
314317
builder.setPartitionOptions(pbuilder.build());
315318

319+
XGoogSpannerRequestId reqId =
320+
session.getRequestIdCreator().nextRequestId(session.getChannel(), 1);
316321
final PartitionQueryRequest request = builder.build();
317322
try {
318-
PartitionResponse response = rpc.partitionQuery(request, options);
323+
PartitionResponse response = rpc.partitionQuery(request, reqId.withOptions(options));
319324
ImmutableList.Builder<Partition> partitions = ImmutableList.builder();
320325
for (com.google.spanner.v1.Partition p : response.getPartitionsList()) {
321326
Partition partition =
@@ -328,6 +333,7 @@ private List<Partition> partitionQuery(
328333
if (!isFallback && maybeMarkUnimplementedForPartitionedOps(e)) {
329334
return partitionQuery(partitionOptions, statement, true, option);
330335
}
336+
e.setRequestId(reqId);
331337
throw e;
332338
}
333339
}

google-cloud-spanner/src/main/java/com/google/cloud/spanner/PartitionedDmlTransaction.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,12 @@ long executeStreamingPartitionedUpdate(
7979
boolean foundStats = false;
8080
long updateCount = 0L;
8181
Stopwatch stopwatch = Stopwatch.createStarted(ticker);
82-
Options options = Options.fromUpdateOptions(updateOptions);
82+
XGoogSpannerRequestId reqId =
83+
session.getRequestIdCreator().nextRequestId(session.getChannel(), 1);
84+
UpdateOption[] allOptions = new UpdateOption[updateOptions.length + 1];
85+
System.arraycopy(updateOptions, 0, allOptions, 0, updateOptions.length);
86+
allOptions[allOptions.length - 1] = new Options.RequestIdOption(reqId);
87+
Options options = Options.fromUpdateOptions(allOptions);
8388

8489
try {
8590
ExecuteSqlRequest request = newTransactionRequestFrom(statement, options);
@@ -89,7 +94,8 @@ long executeStreamingPartitionedUpdate(
8994

9095
try {
9196
ServerStream<PartialResultSet> stream =
92-
rpc.executeStreamingPartitionedDml(request, session.getOptions(), remainingTimeout);
97+
rpc.executeStreamingPartitionedDml(
98+
request, reqId.withOptions(session.getOptions()), remainingTimeout);
9399

94100
for (PartialResultSet rs : stream) {
95101
if (rs.getResumeToken() != null && !rs.getResumeToken().isEmpty()) {
@@ -104,6 +110,7 @@ long executeStreamingPartitionedUpdate(
104110
} catch (UnavailableException e) {
105111
LOGGER.log(
106112
Level.FINER, "Retrying PartitionedDml transaction after UnavailableException", e);
113+
reqId.incrementAttempt();
107114
request = resumeOrRestartRequest(resumeToken, statement, request, options);
108115
} catch (InternalException e) {
109116
if (!isRetryableInternalErrorPredicate.apply(e)) {
@@ -112,24 +119,31 @@ long executeStreamingPartitionedUpdate(
112119

113120
LOGGER.log(
114121
Level.FINER, "Retrying PartitionedDml transaction after InternalException - EOS", e);
122+
reqId.incrementAttempt();
115123
request = resumeOrRestartRequest(resumeToken, statement, request, options);
116124
} catch (AbortedException e) {
117125
LOGGER.log(Level.FINER, "Retrying PartitionedDml transaction after AbortedException", e);
118126
resumeToken = ByteString.EMPTY;
119127
foundStats = false;
120128
updateCount = 0L;
121129
request = newTransactionRequestFrom(statement, options);
130+
// Create a new xGoogSpannerRequestId.
131+
reqId = session.getRequestIdCreator().nextRequestId(session.getChannel(), 1);
132+
} catch (SpannerException e) {
133+
e.setRequestId(reqId);
134+
throw e;
122135
}
123136
}
124137
if (!foundStats) {
125138
throw SpannerExceptionFactory.newSpannerException(
126139
ErrorCode.INVALID_ARGUMENT,
127-
"Partitioned DML response missing stats possibly due to non-DML statement as input");
140+
"Partitioned DML response missing stats possibly due to non-DML statement as input",
141+
reqId);
128142
}
129143
LOGGER.log(Level.FINER, "Finished PartitionedUpdate statement");
130144
return updateCount;
131145
} catch (Exception e) {
132-
throw SpannerExceptionFactory.newSpannerException(e);
146+
throw SpannerExceptionFactory.newSpannerException(e, reqId);
133147
}
134148
}
135149

@@ -209,11 +223,14 @@ private ByteString initTransaction(final Options options) {
209223
.setExcludeTxnFromChangeStreams(
210224
options.withExcludeTxnFromChangeStreams() == Boolean.TRUE))
211225
.build();
212-
Transaction tx = rpc.beginTransaction(request, session.getOptions(), true);
226+
XGoogSpannerRequestId reqId =
227+
session.getRequestIdCreator().nextRequestId(session.getChannel(), 1);
228+
Transaction tx = rpc.beginTransaction(request, reqId.withOptions(session.getOptions()), true);
213229
if (tx.getId().isEmpty()) {
214230
throw SpannerExceptionFactory.newSpannerException(
215231
ErrorCode.INTERNAL,
216-
"Failed to init transaction, missing transaction id\n" + session.getName());
232+
"Failed to init transaction, missing transaction id\n" + session.getName(),
233+
reqId);
217234
}
218235
return tx.getId();
219236
}

0 commit comments

Comments
 (0)