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

Commit 35cee8e

Browse files
committed
Merge branch 'main' into PR #4026 to update
2 parents 3f93b8a + 6356ef2 commit 35cee8e

19 files changed

Lines changed: 791 additions & 28 deletions

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

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -693,17 +693,15 @@ QueryOptions buildQueryOptions(QueryOptions requestOptions) {
693693
}
694694

695695
RequestOptions buildRequestOptions(Options options) {
696-
// Shortcut for the most common return value.
697-
if (!(options.hasPriority() || options.hasTag() || getTransactionTag() != null)) {
698-
return RequestOptions.getDefaultInstance();
699-
}
700-
701-
RequestOptions.Builder builder = RequestOptions.newBuilder();
702-
if (options.hasPriority()) {
703-
builder.setPriority(options.priority());
704-
}
705-
if (options.hasTag()) {
706-
builder.setRequestTag(options.tag());
696+
RequestOptions.Builder builder = options.toRequestOptionsProto(false).toBuilder();
697+
RequestOptions.ClientContext defaultClientContext =
698+
session.getSpanner().getOptions().getClientContext();
699+
if (defaultClientContext != null) {
700+
RequestOptions.ClientContext.Builder clientContextBuilder = defaultClientContext.toBuilder();
701+
if (builder.hasClientContext()) {
702+
clientContextBuilder.mergeFrom(builder.getClientContext());
703+
}
704+
builder.setClientContext(clientContextBuilder.build());
707705
}
708706
if (getTransactionTag() != null) {
709707
builder.setTransactionTag(getTransactionTag());

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

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.spanner.v1.DirectedReadOptions;
2121
import com.google.spanner.v1.ReadRequest.LockHint;
2222
import com.google.spanner.v1.ReadRequest.OrderBy;
23+
import com.google.spanner.v1.RequestOptions;
2324
import com.google.spanner.v1.RequestOptions.Priority;
2425
import com.google.spanner.v1.TransactionOptions.IsolationLevel;
2526
import com.google.spanner.v1.TransactionOptions.ReadWrite.ReadLockMode;
@@ -265,6 +266,37 @@ public static ReadQueryUpdateTransactionOption priority(RpcPriority priority) {
265266
return new PriorityOption(priority);
266267
}
267268

269+
/**
270+
* Specifying this will add the given client context to the request. The client context is used to
271+
* pass side-channel or configuration information to the backend, such as a user ID for a
272+
* parameterized secure view.
273+
*/
274+
public static ReadQueryUpdateTransactionOption clientContext(
275+
RequestOptions.ClientContext clientContext) {
276+
return new ClientContextOption(clientContext);
277+
}
278+
279+
RequestOptions toRequestOptionsProto(boolean isTransactionOption) {
280+
if (!hasPriority() && !hasTag() && !hasClientContext()) {
281+
return RequestOptions.getDefaultInstance();
282+
}
283+
RequestOptions.Builder builder = RequestOptions.newBuilder();
284+
if (hasPriority()) {
285+
builder.setPriority(priority());
286+
}
287+
if (hasTag()) {
288+
if (isTransactionOption) {
289+
builder.setTransactionTag(tag());
290+
} else {
291+
builder.setRequestTag(tag());
292+
}
293+
}
294+
if (hasClientContext()) {
295+
builder.setClientContext(clientContext());
296+
}
297+
return builder.build();
298+
}
299+
268300
public static TransactionOption maxCommitDelay(Duration maxCommitDelay) {
269301
Preconditions.checkArgument(!maxCommitDelay.isNegative(), "maxCommitDelay should be positive");
270302
return new MaxCommitDelayOption(maxCommitDelay);
@@ -462,6 +494,20 @@ void appendToOptions(Options options) {
462494
}
463495
}
464496

497+
static final class ClientContextOption extends InternalOption
498+
implements ReadQueryUpdateTransactionOption {
499+
private final RequestOptions.ClientContext clientContext;
500+
501+
ClientContextOption(RequestOptions.ClientContext clientContext) {
502+
this.clientContext = clientContext;
503+
}
504+
505+
@Override
506+
void appendToOptions(Options options) {
507+
options.clientContext = clientContext;
508+
}
509+
}
510+
465511
static final class TagOption extends InternalOption implements ReadQueryUpdateTransactionOption {
466512
private final String tag;
467513

@@ -574,6 +620,7 @@ void appendToOptions(Options options) {
574620
private String filter;
575621
private RpcPriority priority;
576622
private String tag;
623+
private RequestOptions.ClientContext clientContext;
577624
private String etag;
578625
private Boolean validateOnly;
579626
private Boolean withExcludeTxnFromChangeStreams;
@@ -666,6 +713,14 @@ Priority priority() {
666713
return priority == null ? null : priority.proto;
667714
}
668715

716+
boolean hasClientContext() {
717+
return clientContext != null;
718+
}
719+
720+
RequestOptions.ClientContext clientContext() {
721+
return clientContext;
722+
}
723+
669724
boolean hasTag() {
670725
return tag != null;
671726
}
@@ -777,6 +832,9 @@ public String toString() {
777832
if (priority != null) {
778833
b.append("priority: ").append(priority).append(' ');
779834
}
835+
if (clientContext != null) {
836+
b.append("clientContext: ").append(clientContext).append(' ');
837+
}
780838
if (tag != null) {
781839
b.append("tag: ").append(tag).append(' ');
782840
}
@@ -850,6 +908,7 @@ public boolean equals(Object o) {
850908
&& Objects.equals(pageToken(), that.pageToken())
851909
&& Objects.equals(filter(), that.filter())
852910
&& Objects.equals(priority(), that.priority())
911+
&& Objects.equals(clientContext(), that.clientContext())
853912
&& Objects.equals(tag(), that.tag())
854913
&& Objects.equals(etag(), that.etag())
855914
&& Objects.equals(validateOnly(), that.validateOnly())
@@ -894,6 +953,9 @@ public int hashCode() {
894953
if (priority != null) {
895954
result = 31 * result + priority.hashCode();
896955
}
956+
if (clientContext != null) {
957+
result = 31 * result + clientContext.hashCode();
958+
}
897959
if (tag != null) {
898960
result = 31 * result + tag.hashCode();
899961
}

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

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import com.google.cloud.spanner.SessionClient.SessionOption;
3333
import com.google.cloud.spanner.TransactionRunnerImpl.TransactionContextImpl;
3434
import com.google.cloud.spanner.spi.v1.SpannerRpc;
35-
import com.google.common.base.Strings;
3635
import com.google.common.base.Ticker;
3736
import com.google.common.collect.Lists;
3837
import com.google.common.util.concurrent.MoreExecutors;
@@ -182,6 +181,10 @@ ErrorHandler getErrorHandler() {
182181
return this.errorHandler;
183182
}
184183

184+
SpannerImpl getSpanner() {
185+
return spanner;
186+
}
187+
185188
void setCurrentSpan(ISpan span) {
186189
currentSpan = span;
187190
}
@@ -486,9 +489,22 @@ ApiFuture<Transaction> beginTransactionAsync(
486489
if (sessionReference.getIsMultiplexed() && mutation != null) {
487490
requestBuilder.setMutationKey(mutation);
488491
}
489-
if (sessionReference.getIsMultiplexed() && !Strings.isNullOrEmpty(transactionOptions.tag())) {
490-
requestBuilder.setRequestOptions(
491-
RequestOptions.newBuilder().setTransactionTag(transactionOptions.tag()).build());
492+
RequestOptions.Builder optionsBuilder =
493+
transactionOptions.toRequestOptionsProto(true).toBuilder();
494+
RequestOptions.ClientContext defaultClientContext = spanner.getOptions().getClientContext();
495+
if (defaultClientContext != null) {
496+
RequestOptions.ClientContext.Builder builder = defaultClientContext.toBuilder();
497+
if (optionsBuilder.hasClientContext()) {
498+
builder.mergeFrom(optionsBuilder.getClientContext());
499+
}
500+
optionsBuilder.setClientContext(builder.build());
501+
}
502+
if (!sessionReference.getIsMultiplexed()) {
503+
optionsBuilder.clearTransactionTag();
504+
}
505+
RequestOptions requestOptions = optionsBuilder.build();
506+
if (!requestOptions.equals(RequestOptions.getDefaultInstance())) {
507+
requestBuilder.setRequestOptions(requestOptions);
492508
}
493509
final BeginTransactionRequest request = requestBuilder.build();
494510
final ApiFuture<Transaction> requestFuture;

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import com.google.spanner.v1.DirectedReadOptions;
6969
import com.google.spanner.v1.ExecuteSqlRequest;
7070
import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions;
71+
import com.google.spanner.v1.RequestOptions;
7172
import com.google.spanner.v1.SpannerGrpc;
7273
import com.google.spanner.v1.TransactionOptions;
7374
import com.google.spanner.v1.TransactionOptions.IsolationLevel;
@@ -227,6 +228,7 @@ public static GcpChannelPoolOptions createDefaultDynamicChannelPoolOptions() {
227228
private final boolean autoThrottleAdministrativeRequests;
228229
private final RetrySettings retryAdministrativeRequestsSettings;
229230
private final boolean trackTransactionStarter;
231+
private final boolean enableGrpcGcpOtelMetrics;
230232
private final BuiltInMetricsProvider builtInMetricsProvider = BuiltInMetricsProvider.INSTANCE;
231233

232234
/**
@@ -259,6 +261,7 @@ public static GcpChannelPoolOptions createDefaultDynamicChannelPoolOptions() {
259261
private final boolean enableEndToEndTracing;
260262
private final String monitoringHost;
261263
private final TransactionOptions defaultTransactionOptions;
264+
private final RequestOptions.ClientContext clientContext;
262265

263266
enum TracingFramework {
264267
OPEN_CENSUS,
@@ -895,6 +898,7 @@ protected SpannerOptions(Builder builder) {
895898
autoThrottleAdministrativeRequests = builder.autoThrottleAdministrativeRequests;
896899
retryAdministrativeRequestsSettings = builder.retryAdministrativeRequestsSettings;
897900
trackTransactionStarter = builder.trackTransactionStarter;
901+
enableGrpcGcpOtelMetrics = builder.enableGrpcGcpOtelMetrics;
898902
defaultQueryOptions = builder.defaultQueryOptions;
899903
envQueryOptions = builder.getEnvironmentQueryOptions();
900904
if (envQueryOptions.equals(QueryOptions.getDefaultInstance())) {
@@ -925,13 +929,19 @@ protected SpannerOptions(Builder builder) {
925929
enableEndToEndTracing = builder.enableEndToEndTracing;
926930
monitoringHost = builder.monitoringHost;
927931
defaultTransactionOptions = builder.defaultTransactionOptions;
932+
clientContext = builder.clientContext;
928933
}
929934

930935
private String getResolvedUniverseDomain() {
931936
String universeDomain = getUniverseDomain();
932937
return Strings.isNullOrEmpty(universeDomain) ? GOOGLE_DEFAULT_UNIVERSE : universeDomain;
933938
}
934939

940+
/** Returns the default {@link RequestOptions.ClientContext} for this {@link SpannerOptions}. */
941+
public RequestOptions.ClientContext getClientContext() {
942+
return clientContext;
943+
}
944+
935945
/**
936946
* The environment to read configuration values from. The default implementation uses environment
937947
* variables.
@@ -975,6 +985,10 @@ default boolean isEnableGRPCBuiltInMetrics() {
975985
return false;
976986
}
977987

988+
default boolean isEnableGrpcGcpOtelMetrics() {
989+
return true;
990+
}
991+
978992
default boolean isEnableEndToEndTracing() {
979993
return false;
980994
}
@@ -1013,6 +1027,8 @@ private static class SpannerEnvironmentImpl implements SpannerEnvironment {
10131027
private static final String SPANNER_DISABLE_BUILTIN_METRICS = "SPANNER_DISABLE_BUILTIN_METRICS";
10141028
private static final String SPANNER_DISABLE_DIRECT_ACCESS_GRPC_BUILTIN_METRICS =
10151029
"SPANNER_DISABLE_DIRECT_ACCESS_GRPC_BUILTIN_METRICS";
1030+
private static final String SPANNER_DISABLE_GRPC_GCP_OTEL_METRICS =
1031+
"SPANNER_DISABLE_GRPC_GCP_OTEL_METRICS";
10161032
private static final String SPANNER_MONITORING_HOST = "SPANNER_MONITORING_HOST";
10171033

10181034
private SpannerEnvironmentImpl() {}
@@ -1058,6 +1074,11 @@ public boolean isEnableGRPCBuiltInMetrics() {
10581074
System.getenv(SPANNER_DISABLE_DIRECT_ACCESS_GRPC_BUILTIN_METRICS));
10591075
}
10601076

1077+
@Override
1078+
public boolean isEnableGrpcGcpOtelMetrics() {
1079+
return !Boolean.parseBoolean(System.getenv(SPANNER_DISABLE_GRPC_GCP_OTEL_METRICS));
1080+
}
1081+
10611082
@Override
10621083
public boolean isEnableEndToEndTracing() {
10631084
return Boolean.parseBoolean(System.getenv(SPANNER_ENABLE_END_TO_END_TRACING));
@@ -1128,6 +1149,8 @@ public static class Builder
11281149
private boolean autoThrottleAdministrativeRequests = false;
11291150
private boolean trackTransactionStarter = false;
11301151
private Map<DatabaseId, QueryOptions> defaultQueryOptions = new HashMap<>();
1152+
private boolean enableGrpcGcpOtelMetrics =
1153+
SpannerOptions.environment.isEnableGrpcGcpOtelMetrics();
11311154
private CallCredentialsProvider callCredentialsProvider;
11321155
private CloseableExecutorProvider asyncExecutorProvider;
11331156
private String compressorName;
@@ -1146,6 +1169,7 @@ public static class Builder
11461169
private String experimentalHost = null;
11471170
private boolean usePlainText = false;
11481171
private TransactionOptions defaultTransactionOptions = TransactionOptions.getDefaultInstance();
1172+
private RequestOptions.ClientContext clientContext;
11491173

11501174
private static String createCustomClientLibToken(String token) {
11511175
return token + " " + ServiceOptions.getGoogApiClientLibName();
@@ -1231,6 +1255,7 @@ protected Builder() {
12311255
this.autoThrottleAdministrativeRequests = options.autoThrottleAdministrativeRequests;
12321256
this.retryAdministrativeRequestsSettings = options.retryAdministrativeRequestsSettings;
12331257
this.trackTransactionStarter = options.trackTransactionStarter;
1258+
this.enableGrpcGcpOtelMetrics = options.enableGrpcGcpOtelMetrics;
12341259
this.defaultQueryOptions = options.defaultQueryOptions;
12351260
this.callCredentialsProvider = options.callCredentialsProvider;
12361261
this.asyncExecutorProvider = options.asyncExecutorProvider;
@@ -1248,6 +1273,7 @@ protected Builder() {
12481273
this.enableEndToEndTracing = options.enableEndToEndTracing;
12491274
this.monitoringHost = options.monitoringHost;
12501275
this.defaultTransactionOptions = options.defaultTransactionOptions;
1276+
this.clientContext = options.clientContext;
12511277
}
12521278

12531279
@Override
@@ -1750,6 +1776,17 @@ public Builder disableDynamicChannelPool() {
17501776
return this;
17511777
}
17521778

1779+
/**
1780+
* Sets whether to enable or disable grpc-gcp OpenTelemetry metrics injection. When disabled,
1781+
* Spanner will not automatically inject an OpenTelemetry {@link
1782+
* io.opentelemetry.api.metrics.Meter} into grpc-gcp. If a Meter or MetricRegistry is explicitly
1783+
* provided via {@link GcpManagedChannelOptions}, those settings will still be honored.
1784+
*/
1785+
public Builder setGrpcGcpOtelMetricsEnabled(boolean enableGrpcGcpOtelMetrics) {
1786+
this.enableGrpcGcpOtelMetrics = enableGrpcGcpOtelMetrics;
1787+
return this;
1788+
}
1789+
17531790
/**
17541791
* Sets the channel pool options for dynamic channel pooling. Use this to configure the dynamic
17551792
* channel pool behavior when {@link #enableDynamicChannelPool()} is enabled.
@@ -1989,6 +2026,12 @@ public Builder setDefaultTransactionOptions(
19892026
return this;
19902027
}
19912028

2029+
/** Sets the default {@link RequestOptions.ClientContext} for all requests. */
2030+
public Builder setDefaultClientContext(RequestOptions.ClientContext clientContext) {
2031+
this.clientContext = clientContext;
2032+
return this;
2033+
}
2034+
19922035
@SuppressWarnings("rawtypes")
19932036
@Override
19942037
public SpannerOptions build() {
@@ -2211,6 +2254,10 @@ public boolean isGrpcGcpExtensionEnabled() {
22112254
return grpcGcpExtensionEnabled;
22122255
}
22132256

2257+
public boolean isGrpcGcpOtelMetricsEnabled() {
2258+
return enableGrpcGcpOtelMetrics;
2259+
}
2260+
22142261
public GcpManagedChannelOptions getGrpcGcpOptions() {
22152262
return grpcGcpOptions;
22162263
}

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

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -464,15 +464,9 @@ public void run() {
464464
waitForTransactionTimeoutMillis, TimeUnit.MILLISECONDS)
465465
: transactionId);
466466
}
467-
if (options.hasPriority() || getTransactionTag() != null) {
468-
RequestOptions.Builder requestOptionsBuilder = RequestOptions.newBuilder();
469-
if (options.hasPriority()) {
470-
requestOptionsBuilder.setPriority(options.priority());
471-
}
472-
if (getTransactionTag() != null) {
473-
requestOptionsBuilder.setTransactionTag(getTransactionTag());
474-
}
475-
requestBuilder.setRequestOptions(requestOptionsBuilder.build());
467+
RequestOptions requestOptions = options.toRequestOptionsProto(true);
468+
if (!requestOptions.equals(RequestOptions.getDefaultInstance())) {
469+
requestBuilder.setRequestOptions(requestOptions);
476470
}
477471
if (session.getIsMultiplexed() && getLatestPrecommitToken() != null) {
478472
// Set the precommit token in the CommitRequest for multiplexed sessions.

0 commit comments

Comments
 (0)