Skip to content

Commit a049999

Browse files
authored
fix: use a new managed channel builder when creating channels (#13684)
ChannelPoolDpImpl calls the channel supplier many times over a pool's lifetime — at warm-up, on load-driven resize, on capacity misses in newStream, and on channel recycles. gRPC builders are mutating: intercept(...) appends to an internal list, and build() snapshots the current list into the new channel. So if anything installs an interceptor on the shared builder between builds, that interceptor accumulates. If the interceptor includes any headers, this causes all the headers to accumulate.
1 parent 1c395d1 commit a049999

2 files changed

Lines changed: 389 additions & 8 deletions

File tree

java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/channels/SwitchingChannelPool.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import io.grpc.CallOptions;
3030
import io.grpc.ClientInterceptor;
3131
import io.grpc.ManagedChannel;
32-
import io.grpc.ManagedChannelBuilder;
3332
import io.grpc.MethodDescriptor;
3433
import java.time.Duration;
3534
import java.util.concurrent.ScheduledExecutorService;
@@ -190,28 +189,30 @@ private ChannelPool newChannelPoolFromProvider(
190189
private ChannelPool newChannelPoolFromProvider(
191190
ChannelProvider channelProvider, String logName, ClientInterceptor... interceptors) {
192191
if (channelProvider.isSingleEndpoint()) {
193-
return new SingleChannelPool(
194-
channelBuilderToSupplier(channelProvider.newChannelBuilder(), interceptors));
192+
return new SingleChannelPool(channelSupplier(channelProvider, interceptors));
195193
}
196194

197195
if (logName != null) {
198196
return new ChannelPoolDpImpl(
199-
channelBuilderToSupplier(channelProvider.newChannelBuilder(), interceptors),
197+
channelSupplier(channelProvider, interceptors),
200198
currentConfiguration,
201199
logName,
202200
metrics.getDebugTagTracer(),
203201
backgroundExecutor);
204202
}
205203

206204
return new ChannelPoolDpImpl(
207-
channelBuilderToSupplier(channelProvider.newChannelBuilder(), interceptors),
205+
channelSupplier(channelProvider, interceptors),
208206
currentConfiguration,
209207
metrics.getDebugTagTracer(),
210208
backgroundExecutor);
211209
}
212210

213-
private Supplier<ManagedChannel> channelBuilderToSupplier(
214-
ManagedChannelBuilder<?> channelBuilder, ClientInterceptor... interceptors) {
215-
return () -> channelBuilder.intercept(interceptors).build();
211+
// Each supplier invocation must produce a fresh ManagedChannelBuilder. Capturing one builder
212+
// and calling .build() on it repeatedly lets anything that mutates the builder between builds
213+
// accumulate — the Nth channel then ships with N copies of that interceptor.
214+
private Supplier<ManagedChannel> channelSupplier(
215+
ChannelProvider channelProvider, ClientInterceptor... interceptors) {
216+
return () -> channelProvider.newChannelBuilder().intercept(interceptors).build();
216217
}
217218
}

0 commit comments

Comments
 (0)