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

Commit 70e5eb3

Browse files
committed
address comments
1 parent 2bd6884 commit 70e5eb3

3 files changed

Lines changed: 203 additions & 161 deletions

File tree

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ public static GcpChannelPoolOptions createDefaultDynamicChannelPoolOptions() {
253253
private final String compressorName;
254254
private final boolean leaderAwareRoutingEnabled;
255255
private final boolean enableDirectAccess;
256+
private final boolean enableGcpFallback;
256257
private final DirectedReadOptions directedReadOptions;
257258
private final boolean useVirtualThreads;
258259
private final OpenTelemetry openTelemetry;
@@ -918,6 +919,7 @@ protected SpannerOptions(Builder builder) {
918919
compressorName = builder.compressorName;
919920
leaderAwareRoutingEnabled = builder.leaderAwareRoutingEnabled;
920921
enableDirectAccess = builder.enableDirectAccess;
922+
enableGcpFallback = builder.enableGcpFallback;
921923
directedReadOptions = builder.directedReadOptions;
922924
useVirtualThreads = builder.useVirtualThreads;
923925
openTelemetry = builder.openTelemetry;
@@ -980,6 +982,10 @@ default boolean isEnableDirectAccess() {
980982
return false;
981983
}
982984

985+
default boolean isEnableGcpFallback() {
986+
return false;
987+
}
988+
983989
default boolean isEnableBuiltInMetrics() {
984990
return true;
985991
}
@@ -1029,6 +1035,8 @@ private static class SpannerEnvironmentImpl implements SpannerEnvironment {
10291035
private static final String SPANNER_ENABLE_API_TRACING = "SPANNER_ENABLE_API_TRACING";
10301036
private static final String GOOGLE_SPANNER_ENABLE_DIRECT_ACCESS =
10311037
"GOOGLE_SPANNER_ENABLE_DIRECT_ACCESS";
1038+
private static final String GOOGLE_SPANNER_ENABLE_GCP_FALLBACK =
1039+
"GOOGLE_SPANNER_ENABLE_GCP_FALLBACK";
10321040
private static final String SPANNER_ENABLE_END_TO_END_TRACING =
10331041
"SPANNER_ENABLE_END_TO_END_TRACING";
10341042
private static final String SPANNER_DISABLE_BUILTIN_METRICS = "SPANNER_DISABLE_BUILTIN_METRICS";
@@ -1068,6 +1076,11 @@ public boolean isEnableDirectAccess() {
10681076
return Boolean.parseBoolean(System.getenv(GOOGLE_SPANNER_ENABLE_DIRECT_ACCESS));
10691077
}
10701078

1079+
@Override
1080+
public boolean isEnableGcpFallback() {
1081+
return Boolean.parseBoolean(System.getenv(GOOGLE_SPANNER_ENABLE_GCP_FALLBACK));
1082+
}
1083+
10711084
@Override
10721085
public boolean isEnableBuiltInMetrics() {
10731086
return !Boolean.parseBoolean(System.getenv(SPANNER_DISABLE_BUILTIN_METRICS));
@@ -1169,6 +1182,7 @@ public static class Builder
11691182
private String emulatorHost = System.getenv("SPANNER_EMULATOR_HOST");
11701183
private boolean leaderAwareRoutingEnabled = true;
11711184
private boolean enableDirectAccess = SpannerOptions.environment.isEnableDirectAccess();
1185+
private boolean enableGcpFallback = SpannerOptions.environment.isEnableGcpFallback();
11721186
private DirectedReadOptions directedReadOptions;
11731187
private boolean useVirtualThreads = false;
11741188
private OpenTelemetry openTelemetry;
@@ -1278,6 +1292,7 @@ protected Builder() {
12781292
this.channelConfigurator = options.channelConfigurator;
12791293
this.interceptorProvider = options.interceptorProvider;
12801294
this.enableDirectAccess = options.enableDirectAccess;
1295+
this.enableGcpFallback = options.enableGcpFallback;
12811296
this.directedReadOptions = options.directedReadOptions;
12821297
this.useVirtualThreads = options.useVirtualThreads;
12831298
this.enableApiTracing = options.enableApiTracing;
@@ -2338,6 +2353,10 @@ public Boolean isEnableDirectAccess() {
23382353
return enableDirectAccess;
23392354
}
23402355

2356+
public Boolean isEnableGcpFallback() {
2357+
return enableGcpFallback;
2358+
}
2359+
23412360
@ObsoleteApi("Use isEnableDirectAccess() instead")
23422361
@Deprecated
23432362
public boolean isAttemptDirectPath() {

google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java

Lines changed: 96 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -370,85 +370,12 @@ public GapicSpannerRpc(final SpannerOptions options) {
370370

371371
if (options.getChannelProvider() == null
372372
&& isEnableDirectAccess
373-
&& isEnableGcpFallbackEnv()) {
374-
InstantiatingGrpcChannelProvider.Builder cloudPathProviderBuilder =
375-
createChannelProviderBuilder(
376-
options, headerProviderWithUserAgent, /* isEnableDirectAccess= */ false);
377-
378-
final AtomicReference<ManagedChannelBuilder> cloudPathBuilderRef = new AtomicReference<>();
379-
cloudPathProviderBuilder.setChannelConfigurator(
380-
builder -> {
381-
if (options.getChannelConfigurator() != null) {
382-
builder = options.getChannelConfigurator().apply(builder);
383-
}
384-
cloudPathBuilderRef.set(builder);
385-
return builder;
386-
});
387-
388-
// Build the cloudPathProvider to extract the builder which will be provided to
389-
// FallbackChannelBuilder.
390-
try (TransportChannel ignored = cloudPathProviderBuilder.build().getTransportChannel()) {
391-
} catch (Exception e) {
392-
throw asSpannerException(e);
393-
}
394-
395-
ManagedChannelBuilder cloudPathBuilder = cloudPathBuilderRef.get();
396-
if (cloudPathBuilder == null) {
397-
throw new IllegalStateException("CloudPath builder was not captured.");
398-
}
399-
400-
try {
401-
Credentials credentials = credentialsProvider.getCredentials();
402-
if (credentials != null) {
403-
cloudPathBuilder.intercept(
404-
new ClientInterceptor() {
405-
@Override
406-
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
407-
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
408-
return next.newCall(
409-
method,
410-
callOptions.withCallCredentials(MoreCallCredentials.from(credentials)));
411-
}
412-
});
413-
}
414-
} catch (Exception e) {
415-
throw asSpannerException(e);
416-
}
417-
418-
defaultChannelProviderBuilder.setChannelConfigurator(
419-
directPathBuilder -> {
420-
if (options.getChannelConfigurator() != null) {
421-
directPathBuilder = options.getChannelConfigurator().apply(directPathBuilder);
422-
}
423-
424-
String jsonApiConfig = parseGrpcGcpApiConfig();
425-
GcpManagedChannelOptions gcpOptions = grpcGcpOptionsWithMetricsAndDcp(options);
426-
if (gcpOptions == null) {
427-
gcpOptions = GcpManagedChannelOptions.newBuilder().build();
428-
}
429-
430-
GcpManagedChannelBuilder primaryGcpBuilder =
431-
GcpManagedChannelBuilder.forDelegateBuilder(directPathBuilder)
432-
.withApiConfigJsonString(jsonApiConfig)
433-
.withOptions(gcpOptions);
434-
435-
GcpManagedChannelBuilder fallbackGcpBuilder =
436-
GcpManagedChannelBuilder.forDelegateBuilder(cloudPathBuilder)
437-
.withApiConfigJsonString(jsonApiConfig)
438-
.withOptions(gcpOptions);
439-
440-
GcpFallbackOpenTelemetry fallbackTelemetry =
441-
GcpFallbackOpenTelemetry.newBuilder()
442-
.withSdk(options.getOpenTelemetry())
443-
.disableAllMetrics()
444-
.enableMetrics(Arrays.asList("fallback_count", "call_status"))
445-
.build();
446-
447-
return new FallbackChannelBuilder(
448-
primaryGcpBuilder,
449-
fallbackGcpBuilder,
450-
createFallbackChannelOptions(fallbackTelemetry, 1));
451-
});
373+
&& options.isEnableGcpFallback()) {
374+
setupGcpFallback(
375+
defaultChannelProviderBuilder,
376+
options,
377+
headerProviderWithUserAgent,
378+
credentialsProvider);
452379
}
453380

454381
boolean enableLocationApi = options.isEnableLocationApi();
@@ -666,6 +593,96 @@ private static String parseGrpcGcpApiConfig() {
666593
}
667594
}
668595

596+
private void setupGcpFallback(
597+
InstantiatingGrpcChannelProvider.Builder defaultChannelProviderBuilder,
598+
final SpannerOptions options,
599+
final HeaderProvider headerProviderWithUserAgent,
600+
final CredentialsProvider credentialsProvider) {
601+
InstantiatingGrpcChannelProvider.Builder cloudPathProviderBuilder =
602+
createChannelProviderBuilder(
603+
options, headerProviderWithUserAgent, /* isEnableDirectAccess= */ false);
604+
605+
final ApiFunction<ManagedChannelBuilder, ManagedChannelBuilder> existingCloudPathConfigurator =
606+
cloudPathProviderBuilder.getChannelConfigurator();
607+
final AtomicReference<ManagedChannelBuilder> cloudPathBuilderRef = new AtomicReference<>();
608+
cloudPathProviderBuilder.setChannelConfigurator(
609+
builder -> {
610+
ManagedChannelBuilder effectiveBuilder = builder;
611+
if (existingCloudPathConfigurator != null) {
612+
effectiveBuilder = existingCloudPathConfigurator.apply(effectiveBuilder);
613+
}
614+
cloudPathBuilderRef.set(effectiveBuilder);
615+
return effectiveBuilder;
616+
});
617+
618+
// Build the cloudPathProvider to extract the builder which will be provided to
619+
// FallbackChannelBuilder.
620+
try (TransportChannel ignored = cloudPathProviderBuilder.build().getTransportChannel()) {
621+
} catch (Exception e) {
622+
throw asSpannerException(e);
623+
}
624+
625+
ManagedChannelBuilder cloudPathBuilder = cloudPathBuilderRef.get();
626+
if (cloudPathBuilder == null) {
627+
throw new IllegalStateException("CloudPath builder was not captured.");
628+
}
629+
630+
try {
631+
Credentials credentials = credentialsProvider.getCredentials();
632+
if (credentials != null) {
633+
cloudPathBuilder.intercept(
634+
new ClientInterceptor() {
635+
@Override
636+
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
637+
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
638+
return next.newCall(
639+
method, callOptions.withCallCredentials(MoreCallCredentials.from(credentials)));
640+
}
641+
});
642+
}
643+
} catch (Exception e) {
644+
throw asSpannerException(e);
645+
}
646+
647+
final ApiFunction<ManagedChannelBuilder, ManagedChannelBuilder> existingConfigurator =
648+
defaultChannelProviderBuilder.getChannelConfigurator();
649+
defaultChannelProviderBuilder.setChannelConfigurator(
650+
directPathBuilder -> {
651+
ManagedChannelBuilder builder = directPathBuilder;
652+
if (existingConfigurator != null) {
653+
builder = existingConfigurator.apply(builder);
654+
}
655+
656+
String jsonApiConfig = parseGrpcGcpApiConfig();
657+
GcpManagedChannelOptions gcpOptions = grpcGcpOptionsWithMetricsAndDcp(options);
658+
if (gcpOptions == null) {
659+
gcpOptions = GcpManagedChannelOptions.newBuilder().build();
660+
}
661+
662+
GcpManagedChannelBuilder primaryGcpBuilder =
663+
GcpManagedChannelBuilder.forDelegateBuilder(builder)
664+
.withApiConfigJsonString(jsonApiConfig)
665+
.withOptions(gcpOptions);
666+
667+
GcpManagedChannelBuilder fallbackGcpBuilder =
668+
GcpManagedChannelBuilder.forDelegateBuilder(cloudPathBuilder)
669+
.withApiConfigJsonString(jsonApiConfig)
670+
.withOptions(gcpOptions);
671+
672+
GcpFallbackOpenTelemetry fallbackTelemetry =
673+
GcpFallbackOpenTelemetry.newBuilder()
674+
.withSdk(options.getOpenTelemetry())
675+
.disableAllMetrics()
676+
.enableMetrics(Arrays.asList("fallback_count", "call_status"))
677+
.build();
678+
679+
return new FallbackChannelBuilder(
680+
primaryGcpBuilder,
681+
fallbackGcpBuilder,
682+
createFallbackChannelOptions(fallbackTelemetry, 1));
683+
});
684+
}
685+
669686
private InstantiatingGrpcChannelProvider.Builder createChannelProviderBuilder(
670687
final SpannerOptions options,
671688
final HeaderProvider headerProviderWithUserAgent,
@@ -865,15 +882,6 @@ public static boolean isEnableDirectPathBoundToken() {
865882
return !Boolean.parseBoolean(System.getenv("GOOGLE_SPANNER_DISABLE_DIRECT_ACCESS_BOUND_TOKEN"));
866883
}
867884

868-
@VisibleForTesting static Boolean enableGcpFallbackEnv = null;
869-
870-
public static boolean isEnableGcpFallbackEnv() {
871-
if (enableGcpFallbackEnv != null) {
872-
return enableGcpFallbackEnv;
873-
}
874-
return Boolean.parseBoolean(System.getenv("GOOGLE_SPANNER_ENABLE_GCP_FALLBACK"));
875-
}
876-
877885
private static final RetrySettings ADMIN_REQUESTS_LIMIT_EXCEEDED_RETRY_SETTINGS =
878886
RetrySettings.newBuilder()
879887
.setInitialRetryDelayDuration(Duration.ofSeconds(5L))

0 commit comments

Comments
 (0)