-
Notifications
You must be signed in to change notification settings - Fork 135
feat: add support of dynamic channel pooling #4265
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
90f82b1
2725368
5421d2f
9352441
33a2af2
392308d
482c344
ce43d4a
cf39158
97db5be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -281,6 +281,7 @@ public class GapicSpannerRpc implements SpannerRpc { | |
| private final boolean endToEndTracingEnabled; | ||
| private final int numChannels; | ||
| private final boolean isGrpcGcpExtensionEnabled; | ||
| private final boolean isDynamicChannelPoolEnabled; | ||
|
|
||
| private final GrpcCallContext baseGrpcCallContext; | ||
|
|
||
|
|
@@ -337,6 +338,7 @@ public GapicSpannerRpc(final SpannerOptions options) { | |
| this.endToEndTracingEnabled = options.isEndToEndTracingEnabled(); | ||
| this.numChannels = options.getNumChannels(); | ||
| this.isGrpcGcpExtensionEnabled = options.isGrpcGcpExtensionEnabled(); | ||
| this.isDynamicChannelPoolEnabled = options.isDynamicChannelPoolEnabled(); | ||
| this.baseGrpcCallContext = createBaseCallContext(); | ||
|
|
||
| if (initializeStubs) { | ||
|
|
@@ -475,7 +477,8 @@ public GapicSpannerRpc(final SpannerOptions options) { | |
| .withCheckInterval(pdmlSettings.getStreamWatchdogCheckInterval())); | ||
| } | ||
| this.partitionedDmlStub = | ||
| GrpcSpannerStubWithStubSettingsAndClientContext.create(pdmlSettings.build()); | ||
| GrpcSpannerStubWithStubSettingsAndClientContext.create( | ||
| pdmlSettings.build(), clientContext); | ||
| this.instanceAdminStubSettings = | ||
| options.getInstanceAdminStubSettings().toBuilder() | ||
| .setTransportChannelProvider(channelProvider) | ||
|
|
@@ -569,10 +572,14 @@ private static String parseGrpcGcpApiConfig() { | |
| } | ||
| } | ||
|
|
||
| // Enhance metric options for gRPC-GCP extension. | ||
| private static GcpManagedChannelOptions grpcGcpOptionsWithMetrics(SpannerOptions options) { | ||
| // Enhance gRPC-GCP options with metrics and dynamic channel pool configuration. | ||
| private static GcpManagedChannelOptions grpcGcpOptionsWithMetricsAndDcp(SpannerOptions options) { | ||
| GcpManagedChannelOptions grpcGcpOptions = | ||
| MoreObjects.firstNonNull(options.getGrpcGcpOptions(), new GcpManagedChannelOptions()); | ||
| GcpManagedChannelOptions.Builder optionsBuilder = | ||
| GcpManagedChannelOptions.newBuilder(grpcGcpOptions); | ||
|
|
||
| // Configure metrics options with OpenTelemetry meter | ||
| GcpMetricsOptions metricsOptions = | ||
| MoreObjects.firstNonNull( | ||
| grpcGcpOptions.getMetricsOptions(), GcpMetricsOptions.newBuilder().build()); | ||
|
|
@@ -581,9 +588,21 @@ private static GcpManagedChannelOptions grpcGcpOptionsWithMetrics(SpannerOptions | |
| if (metricsOptions.getNamePrefix().equals("")) { | ||
| metricsOptionsBuilder.withNamePrefix("cloud.google.com/java/spanner/gcp-channel-pool/"); | ||
| } | ||
| return GcpManagedChannelOptions.newBuilder(grpcGcpOptions) | ||
| .withMetricsOptions(metricsOptionsBuilder.build()) | ||
| .build(); | ||
| // Pass OpenTelemetry meter to grpc-gcp for channel pool metrics | ||
| if (metricsOptions.getOpenTelemetryMeter() == null) { | ||
| metricsOptionsBuilder.withOpenTelemetryMeter( | ||
| options.getOpenTelemetry().getMeter("com.google.cloud.spanner")); | ||
| } | ||
| optionsBuilder.withMetricsOptions(metricsOptionsBuilder.build()); | ||
|
|
||
| // Configure dynamic channel pool options if enabled. | ||
| // Uses the GcpChannelPoolOptions from SpannerOptions, which contains Spanner-specific defaults | ||
| // or user-provided configuration. | ||
| if (options.isDynamicChannelPoolEnabled()) { | ||
| optionsBuilder.withChannelPoolOptions(options.getGcpChannelPoolOptions()); | ||
| } | ||
|
|
||
| return optionsBuilder.build(); | ||
| } | ||
|
|
||
| @SuppressWarnings("rawtypes") | ||
|
|
@@ -595,7 +614,11 @@ private static void maybeEnableGrpcGcpExtension( | |
| } | ||
|
|
||
| final String jsonApiConfig = parseGrpcGcpApiConfig(); | ||
| final GcpManagedChannelOptions grpcGcpOptions = grpcGcpOptionsWithMetrics(options); | ||
| final GcpManagedChannelOptions grpcGcpOptions = grpcGcpOptionsWithMetricsAndDcp(options); | ||
|
|
||
| // When dynamic channel pool is enabled, use the DCP initial size as the pool size. | ||
| // When disabled, use the explicitly configured numChannels. | ||
| final int poolSize = options.isDynamicChannelPoolEnabled() ? 0 : options.getNumChannels(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this change the behavior for customers who are currently using grpc-gcp? (I would think not, considering that it seems like the channel pool size was set to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, because when poolSize = 0, grpc-gcp uses initSize from GcpChannelPoolOptions. Previously, numChannels was used as the pool size. The change is intentional to let DCP control initialization. |
||
|
|
||
| ApiFunction<ManagedChannelBuilder, ManagedChannelBuilder> apiFunction = | ||
| channelBuilder -> { | ||
|
|
@@ -605,7 +628,7 @@ private static void maybeEnableGrpcGcpExtension( | |
| return GcpManagedChannelBuilder.forDelegateBuilder(channelBuilder) | ||
| .withApiConfigJsonString(jsonApiConfig) | ||
| .withOptions(grpcGcpOptions) | ||
| .setPoolSize(options.getNumChannels()); | ||
| .setPoolSize(poolSize); | ||
| }; | ||
|
|
||
| // Disable the GAX channel pooling functionality by setting the GAX channel pool size to 1. | ||
|
|
@@ -2060,20 +2083,34 @@ <ReqT, RespT> GrpcCallContext newCallContext( | |
| if (affinity != null) { | ||
| if (this.isGrpcGcpExtensionEnabled) { | ||
| // Set channel affinity in gRPC-GCP. | ||
| // Compute bounded channel hint to prevent gRPC-GCP affinity map from getting unbounded. | ||
| int boundedChannelHint = affinity.intValue() % this.numChannels; | ||
| String affinityKey; | ||
| if (this.isDynamicChannelPoolEnabled) { | ||
| // When dynamic channel pooling is enabled, we use the raw affinity value as the key. | ||
| // This allows grpc-gcp to use round-robin for new keys, enabling new channels | ||
| // (created during scale-up) to receive requests. The affinity key lifetime setting | ||
| // ensures the affinity map doesn't grow unbounded. | ||
| affinityKey = String.valueOf(affinity); | ||
| } else { | ||
| // When DCP is disabled, compute bounded channel hint to prevent | ||
| // gRPC-GCP affinity map from getting unbounded. | ||
| int boundedChannelHint = affinity.intValue() % this.numChannels; | ||
| affinityKey = String.valueOf(boundedChannelHint); | ||
| } | ||
| context = | ||
| context.withCallOptions( | ||
| context | ||
| .getCallOptions() | ||
| .withOption( | ||
| GcpManagedChannel.AFFINITY_KEY, String.valueOf(boundedChannelHint))); | ||
| context.getCallOptions().withOption(GcpManagedChannel.AFFINITY_KEY, affinityKey)); | ||
| } else { | ||
| // Set channel affinity in GAX. | ||
| context = context.withChannelAffinity(affinity.intValue()); | ||
| } | ||
| } | ||
| int requestIdChannel = convertToRequestIdChannelNumber(affinity); | ||
| // When grpc-gcp extension with dynamic channel pooling is enabled, the actual channel ID | ||
| // will be set by RequestIdInterceptor after grpc-gcp selects the channel. | ||
| // Set to 0 (unknown) here as a placeholder. | ||
| int requestIdChannel = | ||
| (this.isGrpcGcpExtensionEnabled && this.isDynamicChannelPoolEnabled) | ||
| ? 0 | ||
| : convertToRequestIdChannelNumber(affinity); | ||
| if (requestId == null) { | ||
| requestId = requestIdCreator.nextRequestId(requestIdChannel); | ||
| } else { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.