From 03e341c2e801d3e7a550b1c6c36884ad10561ef7 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Fri, 17 Apr 2026 16:17:17 -0400 Subject: [PATCH 01/12] feat(gax-grpc): add configurable resize delta and warning for repeated resizing --- sdk-platform-java/gax-java/gax-grpc/pom.xml | 5 - .../com/google/api/gax/grpc/ChannelPool.java | 20 ++- .../api/gax/grpc/ChannelPoolSettings.java | 20 ++- .../google/api/gax/grpc/ChannelPoolTest.java | 166 ++++++++++++++++++ 4 files changed, 202 insertions(+), 9 deletions(-) diff --git a/sdk-platform-java/gax-java/gax-grpc/pom.xml b/sdk-platform-java/gax-java/gax-grpc/pom.xml index af5e752d774e..2d6b84189971 100644 --- a/sdk-platform-java/gax-java/gax-grpc/pom.xml +++ b/sdk-platform-java/gax-java/gax-grpc/pom.xml @@ -142,11 +142,6 @@ org.apache.maven.plugins maven-surefire-plugin - - - !InstantiatingGrpcChannelProviderTest#testLogDirectPathMisconfig_AttemptDirectPathNotSetAndAttemptDirectPathXdsSetViaEnv_warns,!InstantiatingGrpcChannelProviderTest#canUseDirectPath_directPathEnvVarNotSet_attemptDirectPathIsTrue,InstantiatingGrpcChannelProviderTest#testLogDirectPathMisconfigWrongCredential - - diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java index 74e1751db993..baf14d15b916 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java @@ -84,6 +84,8 @@ class ChannelPool extends ManagedChannel { private final AtomicInteger indexTicker = new AtomicInteger(); private final String authority; + private int consecutiveResizes = 0; + static ChannelPool create( ChannelPoolSettings settings, ChannelFactory channelFactory, @@ -313,9 +315,21 @@ void resize() { int currentSize = localEntries.size(); int delta = tentativeTarget - currentSize; int dampenedTarget = tentativeTarget; - if (Math.abs(delta) > ChannelPoolSettings.MAX_RESIZE_DELTA) { - dampenedTarget = - currentSize + (int) Math.copySign(ChannelPoolSettings.MAX_RESIZE_DELTA, delta); + if (Math.abs(delta) > settings.getMaxResizeDelta()) { + dampenedTarget = currentSize + (int) Math.copySign(settings.getMaxResizeDelta(), delta); + } + + boolean resized = (localEntries.size() < minChannels || localEntries.size() > maxChannels); + if (resized) { + consecutiveResizes++; + } else { + consecutiveResizes = 0; + } + + if (consecutiveResizes == 5) { + LOG.warning( + "Channel pool is repeatedly resizing. Consider adjusting `initialChannelCount` or `maxResizeDelta` to a more reasonable value. " + + "See https://docs.cloud.google.com/java/docs/troubleshooting to enable logging and set `com.google.api.gax.grpc.ChannelPool.level=FINEST` to log the channel pool resize behavior."); } // Only resize the pool when thresholds are crossed diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java index ebdc48cdd69e..bcd4fbcf7bee 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java @@ -92,6 +92,15 @@ public abstract class ChannelPoolSettings { */ public abstract int getMaxChannelCount(); + /** + * The maximum number of channels that can be added or removed at a time. + * + *

This setting limits the rate at which the channel pool can grow or shrink in a single resize + * period. The default value is 2. Regardless of this setting, the number of channels will never + * exceed {@link #getMaxChannelCount()}. + */ + public abstract int getMaxResizeDelta(); + /** * The initial size of the channel pool. * @@ -132,6 +141,7 @@ public static ChannelPoolSettings staticallySized(int size) { .setMaxRpcsPerChannel(Integer.MAX_VALUE) .setMinChannelCount(size) .setMaxChannelCount(size) + .setMaxResizeDelta(Math.min(2, size)) .build(); } @@ -142,7 +152,8 @@ public static Builder builder() { .setMaxChannelCount(200) .setMinRpcsPerChannel(0) .setMaxRpcsPerChannel(Integer.MAX_VALUE) - .setPreemptiveRefreshEnabled(false); + .setPreemptiveRefreshEnabled(false) + .setMaxResizeDelta(2); } @AutoValue.Builder @@ -159,6 +170,8 @@ public abstract static class Builder { public abstract Builder setPreemptiveRefreshEnabled(boolean enabled); + public abstract Builder setMaxResizeDelta(int count); + abstract ChannelPoolSettings autoBuild(); public ChannelPoolSettings build() { @@ -178,6 +191,11 @@ public ChannelPoolSettings build() { "initial channel count must be less than maxChannelCount"); Preconditions.checkState( s.getInitialChannelCount() > 0, "Initial channel count must be greater than 0"); + Preconditions.checkState( + s.getMaxResizeDelta() > 0, "Max resize delta must be greater than 0"); + Preconditions.checkState( + s.getMaxResizeDelta() <= s.getMaxChannelCount(), + "Max resize delta cannot be greater than max channel count"); return s; } } diff --git a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ChannelPoolTest.java b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ChannelPoolTest.java index 55a99c1481ec..1219d3679d92 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ChannelPoolTest.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ChannelPoolTest.java @@ -523,6 +523,53 @@ void channelCountShouldNotChangeWhenOutstandingRpcsAreWithinLimits() throws Exce assertThat(pool.entries.get()).hasSize(2); } + @Test + void customResizeDeltaIsRespected() throws Exception { + ScheduledExecutorService executor = Mockito.mock(ScheduledExecutorService.class); + FixedExecutorProvider provider = FixedExecutorProvider.create(executor); + + List channels = new ArrayList<>(); + List> startedCalls = new ArrayList<>(); + + ChannelFactory channelFactory = + () -> { + ManagedChannel channel = Mockito.mock(ManagedChannel.class); + Mockito.when(channel.newCall(Mockito.any(), Mockito.any())) + .thenAnswer( + invocation -> { + @SuppressWarnings("unchecked") + ClientCall clientCall = Mockito.mock(ClientCall.class); + startedCalls.add(clientCall); + return clientCall; + }); + + channels.add(channel); + return channel; + }; + + pool = + new ChannelPool( + ChannelPoolSettings.builder() + .setInitialChannelCount(2) + .setMinRpcsPerChannel(1) + .setMaxRpcsPerChannel(2) + .setMaxResizeDelta(5) + .build(), + channelFactory, + provider); + assertThat(pool.entries.get()).hasSize(2); + + // Add 20 RPCs to push expansion + for (int i = 0; i < 20; i++) { + ClientCalls.futureUnaryCall( + pool.newCall(METHOD_RECOGNIZE, CallOptions.DEFAULT), Color.getDefaultInstance()); + } + pool.resize(); + // delta is 15 - 2 = 13. Capped at maxResizeDelta = 5. + // Expected size = 2 + 5 = 7. + assertThat(pool.entries.get()).hasSize(7); + } + @Test void removedIdleChannelsAreShutdown() throws Exception { ScheduledExecutorService executor = Mockito.mock(ScheduledExecutorService.class); @@ -679,6 +726,125 @@ public void onComplete() {} assertThat(e.getMessage()).isEqualTo("Call is already cancelled"); } + @Test + void repeatedResizingLogsWarningOnExpand() throws Exception { + ScheduledExecutorService executor = Mockito.mock(ScheduledExecutorService.class); + FixedExecutorProvider provider = FixedExecutorProvider.create(executor); + + List channels = new ArrayList<>(); + List> startedCalls = new ArrayList<>(); + + ChannelFactory channelFactory = + () -> { + ManagedChannel channel = Mockito.mock(ManagedChannel.class); + Mockito.when(channel.newCall(Mockito.any(), Mockito.any())) + .thenAnswer( + invocation -> { + @SuppressWarnings("unchecked") + ClientCall clientCall = Mockito.mock(ClientCall.class); + startedCalls.add(clientCall); + return clientCall; + }); + + channels.add(channel); + return channel; + }; + + pool = + new ChannelPool( + ChannelPoolSettings.builder() + .setInitialChannelCount(1) + .setMinRpcsPerChannel(1) + .setMaxRpcsPerChannel(2) + .setMaxResizeDelta(1) + .setMinChannelCount(1) + .setMaxChannelCount(10) + .build(), + channelFactory, + provider); + assertThat(pool.entries.get()).hasSize(1); + + FakeLogHandler logHandler = new FakeLogHandler(); + ChannelPool.LOG.addHandler(logHandler); + + try { + // Add 20 RPCs to push expansion + for (int i = 0; i < 20; i++) { + ClientCalls.futureUnaryCall( + pool.newCall(METHOD_RECOGNIZE, CallOptions.DEFAULT), Color.getDefaultInstance()); + } + + // Resize 4 times, should not log warning yet + for (int i = 0; i < 4; i++) { + pool.resize(); + } + assertThat(logHandler.getAllMessages()).isEmpty(); + + // 5th resize, should log warning + pool.resize(); + assertThat(logHandler.getAllMessages()).hasSize(1); + assertThat(logHandler.getAllMessages()) + .contains( + "Channel pool is repeatedly resizing. Consider adjusting `initialChannelCount` or `maxResizeDelta` to a more reasonable value. " + + "See https://docs.cloud.google.com/java/docs/troubleshooting to enable logging and set `com.google.api.gax.grpc.ChannelPool.level=FINEST` to log the channel pool resize behavior."); + + // 6th resize, should not log again + pool.resize(); + assertThat(logHandler.getAllMessages()).hasSize(1); + } finally { + ChannelPool.LOG.removeHandler(logHandler); + } + } + + @Test + void repeatedResizingLogsWarningOnShrink() throws Exception { + ScheduledExecutorService executor = Mockito.mock(ScheduledExecutorService.class); + FixedExecutorProvider provider = FixedExecutorProvider.create(executor); + + List channels = new ArrayList<>(); + ChannelFactory channelFactory = + () -> { + ManagedChannel channel = Mockito.mock(ManagedChannel.class); + channels.add(channel); + return channel; + }; + + pool = + new ChannelPool( + ChannelPoolSettings.builder() + .setInitialChannelCount(10) + .setMinRpcsPerChannel(1) + .setMaxRpcsPerChannel(2) + .setMaxResizeDelta(1) + .setMinChannelCount(1) + .setMaxChannelCount(10) + .build(), + channelFactory, + provider); + assertThat(pool.entries.get()).hasSize(10); + + FakeLogHandler logHandler = new FakeLogHandler(); + ChannelPool.LOG.addHandler(logHandler); + + try { + // 0 RPCs, should shrink every cycle + // Resize 4 times, should not log warning yet + for (int i = 0; i < 4; i++) { + pool.resize(); + } + assertThat(logHandler.getAllMessages()).isEmpty(); + + // 5th resize, should log warning + pool.resize(); + assertThat(logHandler.getAllMessages()) + .contains( + "Channel pool is repeatedly resizing. Consider adjusting `initialChannelCount` or `maxResizeDelta` to a more reasonable value. " + + "See https://docs.cloud.google.com/java/docs/troubleshooting to enable logging and set `com.google.api.gax.grpc.ChannelPool.level=FINEST` to log the channel pool resize behavior."); + } finally { + ChannelPool.LOG.removeHandler(logHandler); + } + } + @Test void testDoubleRelease() throws Exception { FakeLogHandler logHandler = new FakeLogHandler(); From 2060fcf0ef23999a8465d8f886a6e82c4718c902 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Fri, 17 Apr 2026 16:18:19 -0400 Subject: [PATCH 02/12] chore(gax-grpc): revert surefire plugin configuration in pom.xml --- sdk-platform-java/gax-java/gax-grpc/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdk-platform-java/gax-java/gax-grpc/pom.xml b/sdk-platform-java/gax-java/gax-grpc/pom.xml index 2d6b84189971..af5e752d774e 100644 --- a/sdk-platform-java/gax-java/gax-grpc/pom.xml +++ b/sdk-platform-java/gax-java/gax-grpc/pom.xml @@ -142,6 +142,11 @@ org.apache.maven.plugins maven-surefire-plugin + + + !InstantiatingGrpcChannelProviderTest#testLogDirectPathMisconfig_AttemptDirectPathNotSetAndAttemptDirectPathXdsSetViaEnv_warns,!InstantiatingGrpcChannelProviderTest#canUseDirectPath_directPathEnvVarNotSet_attemptDirectPathIsTrue,InstantiatingGrpcChannelProviderTest#testLogDirectPathMisconfigWrongCredential + + From e876b319da890b4b8d4c4b9cfa843123d6ecee43 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Fri, 17 Apr 2026 16:20:53 -0400 Subject: [PATCH 03/12] chore(gax-grpc): add comments and remove magic numbers for resize delta --- .../src/main/java/com/google/api/gax/grpc/ChannelPool.java | 2 ++ .../java/com/google/api/gax/grpc/ChannelPoolSettings.java | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java index baf14d15b916..2e6bb2515680 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java @@ -84,6 +84,8 @@ class ChannelPool extends ManagedChannel { private final AtomicInteger indexTicker = new AtomicInteger(); private final String authority; + // Tracks the number of consecutive resize cycles where a resize actually occurred (either expand or shrink). + // Used to detect repeated resizing activity and log a warning. private int consecutiveResizes = 0; static ChannelPool create( diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java index bcd4fbcf7bee..ab9dbb15114f 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java @@ -141,7 +141,7 @@ public static ChannelPoolSettings staticallySized(int size) { .setMaxRpcsPerChannel(Integer.MAX_VALUE) .setMinChannelCount(size) .setMaxChannelCount(size) - .setMaxResizeDelta(Math.min(2, size)) + .setMaxResizeDelta(Math.min(MAX_RESIZE_DELTA, size)) .build(); } @@ -153,7 +153,7 @@ public static Builder builder() { .setMinRpcsPerChannel(0) .setMaxRpcsPerChannel(Integer.MAX_VALUE) .setPreemptiveRefreshEnabled(false) - .setMaxResizeDelta(2); + .setMaxResizeDelta(MAX_RESIZE_DELTA); } @AutoValue.Builder From b3ffb641bd5e35cb06ac38559853892e0a0685b4 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Fri, 17 Apr 2026 16:54:39 -0400 Subject: [PATCH 04/12] docs(gax-grpc): explain resizing detection choice in comments --- .../src/main/java/com/google/api/gax/grpc/ChannelPool.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java index 2e6bb2515680..d207003906f6 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java @@ -321,6 +321,9 @@ void resize() { dampenedTarget = currentSize + (int) Math.copySign(settings.getMaxResizeDelta(), delta); } + // We only count as "resized" if the thresholds are crossed and we actually attempt to scale. + // Checking (dampenedTarget != currentSize) would cause false positives when the pool is within + // bounds but not at the target, because the target aims for the middle of the bounds. boolean resized = (localEntries.size() < minChannels || localEntries.size() > maxChannels); if (resized) { consecutiveResizes++; From 8a5daa1d335b2ddab9fe1ae12a122df5dfd3f589 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Fri, 17 Apr 2026 17:00:02 -0400 Subject: [PATCH 05/12] chore(gax-grpc): replace magic number 5 with constant in ChannelPool --- .../src/main/java/com/google/api/gax/grpc/ChannelPool.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java index d207003906f6..86adcebdd7c5 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java @@ -84,6 +84,10 @@ class ChannelPool extends ManagedChannel { private final AtomicInteger indexTicker = new AtomicInteger(); private final String authority; + // The number of consecutive resize cycles to wait before logging a warning about repeated resizing. + // This is an arbitrary value chosen to detect potential thrashing without being too sensitive. + private static final int CONSECUTIVE_RESIZE_THRESHOLD = 5; + // Tracks the number of consecutive resize cycles where a resize actually occurred (either expand or shrink). // Used to detect repeated resizing activity and log a warning. private int consecutiveResizes = 0; @@ -331,7 +335,7 @@ void resize() { consecutiveResizes = 0; } - if (consecutiveResizes == 5) { + if (consecutiveResizes == CONSECUTIVE_RESIZE_THRESHOLD) { LOG.warning( "Channel pool is repeatedly resizing. Consider adjusting `initialChannelCount` or `maxResizeDelta` to a more reasonable value. " + "See https://docs.cloud.google.com/java/docs/troubleshooting to enable logging and set `com.google.api.gax.grpc.ChannelPool.level=FINEST` to log the channel pool resize behavior."); From 813e10176f19491423d9423f3187c1237f314c15 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Fri, 17 Apr 2026 17:05:12 -0400 Subject: [PATCH 06/12] docs(gax-grpc): update javadoc for maxResizeDelta to explain burst handling --- .../src/main/java/com/google/api/gax/grpc/ChannelPool.java | 2 +- .../java/com/google/api/gax/grpc/ChannelPoolSettings.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java index 86adcebdd7c5..d7eda96e5fc6 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java @@ -85,7 +85,7 @@ class ChannelPool extends ManagedChannel { private final String authority; // The number of consecutive resize cycles to wait before logging a warning about repeated resizing. - // This is an arbitrary value chosen to detect potential thrashing without being too sensitive. + // This is an arbitrary value chosen to detect repeated requests for changes (multiple continuous increase or decrease attempts) without being too sensitive. private static final int CONSECUTIVE_RESIZE_THRESHOLD = 5; // Tracks the number of consecutive resize cycles where a resize actually occurred (either expand or shrink). diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java index ab9dbb15114f..518c2d081521 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java @@ -96,8 +96,9 @@ public abstract class ChannelPoolSettings { * The maximum number of channels that can be added or removed at a time. * *

This setting limits the rate at which the channel pool can grow or shrink in a single resize - * period. The default value is 2. Regardless of this setting, the number of channels will never - * exceed {@link #getMaxChannelCount()}. + * period. The default value is 2. Increasing this value can help the pool better handle sudden + * bursts or spikes in requests by allowing it to scale up faster. Regardless of this setting, the + * number of channels will never exceed {@link #getMaxChannelCount()}. */ public abstract int getMaxResizeDelta(); From 91ffb52afed3038a5a4f9b7cec9ba9f5ca2d5567 Mon Sep 17 00:00:00 2001 From: cloud-java-bot Date: Fri, 17 Apr 2026 21:05:02 +0000 Subject: [PATCH 07/12] chore: generate libraries at Fri Apr 17 21:02:58 UTC 2026 --- java-iam-policy/.repo-metadata.json | 1 - java-iam-policy/README.md | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/java-iam-policy/.repo-metadata.json b/java-iam-policy/.repo-metadata.json index 0d797eebf786..bf65bd5c8655 100644 --- a/java-iam-policy/.repo-metadata.json +++ b/java-iam-policy/.repo-metadata.json @@ -10,7 +10,6 @@ "repo": "googleapis/google-cloud-java", "repo_short": "java-iam-policy", "distribution_name": "com.google.cloud:google-iam-policy", - "api_id": "iam.googleapis.com", "library_type": "GAPIC_AUTO", "requires_billing": true, "excluded_dependencies": "google-iam-policy", diff --git a/java-iam-policy/README.md b/java-iam-policy/README.md index a31d56fecfdc..b5f33684f9cf 100644 --- a/java-iam-policy/README.md +++ b/java-iam-policy/README.md @@ -188,7 +188,7 @@ Java is a registered trademark of Oracle and/or its affiliates. [code-of-conduct]: https://github.com/googleapis/google-cloud-java/blob/main/CODE_OF_CONDUCT.md#contributor-code-of-conduct [license]: https://github.com/googleapis/google-cloud-java/blob/main/LICENSE [enable-billing]: https://cloud.google.com/apis/docs/getting-started#enabling_billing -[enable-api]: https://console.cloud.google.com/flows/enableapi?apiid=iam.googleapis.com + [libraries-bom]: https://github.com/GoogleCloudPlatform/cloud-opensource-java/wiki/The-Google-Cloud-Platform-Libraries-BOM [shell_img]: https://gstatic.com/cloudssh/images/open-btn.png From de28aef6a8948724c17af2df1b5a382239f8fd6b Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Fri, 17 Apr 2026 17:07:01 -0400 Subject: [PATCH 08/12] docs(gax-grpc): reference MAX_RESIZE_DELTA constant in javadoc --- .../main/java/com/google/api/gax/grpc/ChannelPool.java | 9 ++++++--- .../com/google/api/gax/grpc/ChannelPoolSettings.java | 7 ++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java index d7eda96e5fc6..4dbb7820e9f0 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java @@ -84,11 +84,14 @@ class ChannelPool extends ManagedChannel { private final AtomicInteger indexTicker = new AtomicInteger(); private final String authority; - // The number of consecutive resize cycles to wait before logging a warning about repeated resizing. - // This is an arbitrary value chosen to detect repeated requests for changes (multiple continuous increase or decrease attempts) without being too sensitive. + // The number of consecutive resize cycles to wait before logging a warning about repeated + // resizing. + // This is an arbitrary value chosen to detect repeated requests for changes (multiple continuous + // increase or decrease attempts) without being too sensitive. private static final int CONSECUTIVE_RESIZE_THRESHOLD = 5; - // Tracks the number of consecutive resize cycles where a resize actually occurred (either expand or shrink). + // Tracks the number of consecutive resize cycles where a resize actually occurred (either expand + // or shrink). // Used to detect repeated resizing activity and log a warning. private int consecutiveResizes = 0; diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java index 518c2d081521..504e0cce7b56 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java @@ -96,9 +96,10 @@ public abstract class ChannelPoolSettings { * The maximum number of channels that can be added or removed at a time. * *

This setting limits the rate at which the channel pool can grow or shrink in a single resize - * period. The default value is 2. Increasing this value can help the pool better handle sudden - * bursts or spikes in requests by allowing it to scale up faster. Regardless of this setting, the - * number of channels will never exceed {@link #getMaxChannelCount()}. + * period. The default value is {@value #MAX_RESIZE_DELTA}. Increasing this value can help the + * pool better handle sudden bursts or spikes in requests by allowing it to scale up faster. + * Regardless of this setting, the number of channels will never exceed {@link + * #getMaxChannelCount()}. */ public abstract int getMaxResizeDelta(); From 827b22d9862e0fe96d0ac9f22d28f725d038b35f Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Fri, 17 Apr 2026 17:09:44 -0400 Subject: [PATCH 09/12] docs(gax-grpc): explain use of == for log threshold --- .../src/main/java/com/google/api/gax/grpc/ChannelPool.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java index 4dbb7820e9f0..660cbb6b0eb3 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java @@ -338,6 +338,8 @@ void resize() { consecutiveResizes = 0; } + // Log warning only once when the threshold is reached to avoid spamming logs. + // Using == instead of >= ensures we don't log on every subsequent resize cycle. if (consecutiveResizes == CONSECUTIVE_RESIZE_THRESHOLD) { LOG.warning( "Channel pool is repeatedly resizing. Consider adjusting `initialChannelCount` or `maxResizeDelta` to a more reasonable value. " From 899736f6d07da7f0d139de880494917f08ff359d Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Fri, 17 Apr 2026 17:27:11 -0400 Subject: [PATCH 10/12] feat(gax-grpc): optimize ChannelPool resize and add thread safety comment --- .../com/google/api/gax/grpc/ChannelPool.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java index 660cbb6b0eb3..f48dbd21074b 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java @@ -84,15 +84,13 @@ class ChannelPool extends ManagedChannel { private final AtomicInteger indexTicker = new AtomicInteger(); private final String authority; - // The number of consecutive resize cycles to wait before logging a warning about repeated - // resizing. - // This is an arbitrary value chosen to detect repeated requests for changes (multiple continuous - // increase or decrease attempts) without being too sensitive. + // The number of consecutive resize cycles to wait before logging a warning about repeated resizing. + // This is an arbitrary value chosen to detect repeated requests for changes (multiple continuous increase or decrease attempts) without being too sensitive. private static final int CONSECUTIVE_RESIZE_THRESHOLD = 5; - // Tracks the number of consecutive resize cycles where a resize actually occurred (either expand - // or shrink). + // Tracks the number of consecutive resize cycles where a resize actually occurred (either expand or shrink). // Used to detect repeated resizing activity and log a warning. + // Note: This field is only accessed within the synchronized resize() method, so it does not need to be atomic. private int consecutiveResizes = 0; static ChannelPool create( @@ -331,7 +329,7 @@ void resize() { // We only count as "resized" if the thresholds are crossed and we actually attempt to scale. // Checking (dampenedTarget != currentSize) would cause false positives when the pool is within // bounds but not at the target, because the target aims for the middle of the bounds. - boolean resized = (localEntries.size() < minChannels || localEntries.size() > maxChannels); + boolean resized = (currentSize < minChannels || currentSize > maxChannels); if (resized) { consecutiveResizes++; } else { @@ -341,9 +339,12 @@ void resize() { // Log warning only once when the threshold is reached to avoid spamming logs. // Using == instead of >= ensures we don't log on every subsequent resize cycle. if (consecutiveResizes == CONSECUTIVE_RESIZE_THRESHOLD) { - LOG.warning( - "Channel pool is repeatedly resizing. Consider adjusting `initialChannelCount` or `maxResizeDelta` to a more reasonable value. " - + "See https://docs.cloud.google.com/java/docs/troubleshooting to enable logging and set `com.google.api.gax.grpc.ChannelPool.level=FINEST` to log the channel pool resize behavior."); + StringBuilder sb = new StringBuilder(); + sb.append("Channel pool is repeatedly resizing. "); + sb.append("Consider adjusting `initialChannelCount` or `maxResizeDelta` to a more reasonable value. "); + sb.append("See https://docs.cloud.google.com/java/docs/troubleshooting to enable logging "); + sb.append("and set `com.google.api.gax.grpc.ChannelPool.level=FINEST` to log the channel pool resize behavior."); + LOG.warning(sb.toString()); } // Only resize the pool when thresholds are crossed From f9792b632f2e1c30e5fba445a37f29dfd8d09a53 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Fri, 17 Apr 2026 17:27:57 -0400 Subject: [PATCH 11/12] style(gax-grpc): format ChannelPool.java --- .../com/google/api/gax/grpc/ChannelPool.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java index f48dbd21074b..030a041dfc07 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java @@ -84,13 +84,17 @@ class ChannelPool extends ManagedChannel { private final AtomicInteger indexTicker = new AtomicInteger(); private final String authority; - // The number of consecutive resize cycles to wait before logging a warning about repeated resizing. - // This is an arbitrary value chosen to detect repeated requests for changes (multiple continuous increase or decrease attempts) without being too sensitive. + // The number of consecutive resize cycles to wait before logging a warning about repeated + // resizing. + // This is an arbitrary value chosen to detect repeated requests for changes (multiple continuous + // increase or decrease attempts) without being too sensitive. private static final int CONSECUTIVE_RESIZE_THRESHOLD = 5; - // Tracks the number of consecutive resize cycles where a resize actually occurred (either expand or shrink). + // Tracks the number of consecutive resize cycles where a resize actually occurred (either expand + // or shrink). // Used to detect repeated resizing activity and log a warning. - // Note: This field is only accessed within the synchronized resize() method, so it does not need to be atomic. + // Note: This field is only accessed within the synchronized resize() method, so it does not need + // to be atomic. private int consecutiveResizes = 0; static ChannelPool create( @@ -341,9 +345,11 @@ void resize() { if (consecutiveResizes == CONSECUTIVE_RESIZE_THRESHOLD) { StringBuilder sb = new StringBuilder(); sb.append("Channel pool is repeatedly resizing. "); - sb.append("Consider adjusting `initialChannelCount` or `maxResizeDelta` to a more reasonable value. "); + sb.append( + "Consider adjusting `initialChannelCount` or `maxResizeDelta` to a more reasonable value. "); sb.append("See https://docs.cloud.google.com/java/docs/troubleshooting to enable logging "); - sb.append("and set `com.google.api.gax.grpc.ChannelPool.level=FINEST` to log the channel pool resize behavior."); + sb.append( + "and set `com.google.api.gax.grpc.ChannelPool.level=FINEST` to log the channel pool resize behavior."); LOG.warning(sb.toString()); } From 856f3f29b0dd06c395c9a7222691c3f3494fa7cb Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Fri, 17 Apr 2026 17:33:25 -0400 Subject: [PATCH 12/12] style(gax-grpc): use constant for warning message and simplify comments --- .../com/google/api/gax/grpc/ChannelPool.java | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java index 030a041dfc07..afd729fc5d48 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPool.java @@ -69,6 +69,11 @@ *

Package-private for internal use. */ class ChannelPool extends ManagedChannel { + private static final String CHANNEL_POOL_CONSECUTIVE_RESIZING_WARNING = + "Channel pool is repeatedly resizing. " + + "Consider adjusting `initialChannelCount` or `maxResizeDelta` to a more reasonable value. " + + "See https://docs.cloud.google.com/java/docs/troubleshooting to enable logging " + + "and set `com.google.api.gax.grpc.ChannelPool.level=FINEST` to log the channel pool resize behavior."; @VisibleForTesting static final Logger LOG = Logger.getLogger(ChannelPool.class.getName()); private static final java.time.Duration REFRESH_PERIOD = java.time.Duration.ofMinutes(50); @@ -85,16 +90,13 @@ class ChannelPool extends ManagedChannel { private final String authority; // The number of consecutive resize cycles to wait before logging a warning about repeated - // resizing. - // This is an arbitrary value chosen to detect repeated requests for changes (multiple continuous - // increase or decrease attempts) without being too sensitive. + // resizing. This is an arbitrary value chosen to detect repeated requests for changes + // (multiple continuous increase or decrease attempts) without being too sensitive. private static final int CONSECUTIVE_RESIZE_THRESHOLD = 5; // Tracks the number of consecutive resize cycles where a resize actually occurred (either expand - // or shrink). - // Used to detect repeated resizing activity and log a warning. - // Note: This field is only accessed within the synchronized resize() method, so it does not need - // to be atomic. + // or shrink). Used to detect repeated resizing activity and log a warning. + // Note: This field is only accessed safely within resizeSafely() and does not need to be atomic. private int consecutiveResizes = 0; static ChannelPool create( @@ -343,14 +345,7 @@ void resize() { // Log warning only once when the threshold is reached to avoid spamming logs. // Using == instead of >= ensures we don't log on every subsequent resize cycle. if (consecutiveResizes == CONSECUTIVE_RESIZE_THRESHOLD) { - StringBuilder sb = new StringBuilder(); - sb.append("Channel pool is repeatedly resizing. "); - sb.append( - "Consider adjusting `initialChannelCount` or `maxResizeDelta` to a more reasonable value. "); - sb.append("See https://docs.cloud.google.com/java/docs/troubleshooting to enable logging "); - sb.append( - "and set `com.google.api.gax.grpc.ChannelPool.level=FINEST` to log the channel pool resize behavior."); - LOG.warning(sb.toString()); + LOG.warning(CHANNEL_POOL_CONSECUTIVE_RESIZING_WARNING); } // Only resize the pool when thresholds are crossed