From 03e341c2e801d3e7a550b1c6c36884ad10561ef7 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Fri, 17 Apr 2026 16:17:17 -0400 Subject: [PATCH 01/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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 From 2be58daca354dcb9a43e3b44c465ca67b078bcd5 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 20 Apr 2026 12:25:13 -0400 Subject: [PATCH 13/22] Revert "chore: generate libraries at Fri Apr 17 21:02:58 UTC 2026" This reverts commit 91ffb52afed3038a5a4f9b7cec9ba9f5ca2d5567. --- java-iam-policy/.repo-metadata.json | 1 + java-iam-policy/README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/java-iam-policy/.repo-metadata.json b/java-iam-policy/.repo-metadata.json index bf65bd5c8655..0d797eebf786 100644 --- a/java-iam-policy/.repo-metadata.json +++ b/java-iam-policy/.repo-metadata.json @@ -10,6 +10,7 @@ "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 b5f33684f9cf..a31d56fecfdc 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 e61486f95aeaa175cb1e0abf1199c7305042b413 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 20 Apr 2026 12:43:19 -0400 Subject: [PATCH 14/22] chore: Update ocmments and refactor --- .../com/google/api/gax/grpc/ChannelPool.java | 20 ++++++++++--------- .../api/gax/grpc/ChannelPoolSettings.java | 15 +++++--------- .../google/api/gax/grpc/ChannelPoolTest.java | 8 ++------ 3 files changed, 18 insertions(+), 25 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 afd729fc5d48..deb7c284626a 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,7 +69,7 @@ *

Package-private for internal use. */ class ChannelPool extends ManagedChannel { - private static final String CHANNEL_POOL_CONSECUTIVE_RESIZING_WARNING = + 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 " @@ -90,8 +90,8 @@ 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 value was 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 @@ -290,7 +290,8 @@ private void resizeSafely() { *

  • Get the maximum number of outstanding RPCs since last invocation *
  • Determine a valid range of number of channels to handle that many outstanding RPCs *
  • If the current number of channel falls outside of that range, add or remove at most - * {@link ChannelPoolSettings#MAX_RESIZE_DELTA} to get closer to middle of that range. + * {@link ChannelPoolSettings#DEFAULT_MAX_RESIZE_DELTA} to get closer to middle of that + * range. * * *

    Not threadsafe, must be called under the entryWriteLock monitor @@ -332,9 +333,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. + // Only count as "resized" if the thresholds are crossed and Gax attempts to scale. Checking + // that `dampenedTarget != currentSize` would cause false positives when the pool is within + // bounds but not at the target (target aims for the middle of the bounds) boolean resized = (currentSize < minChannels || currentSize > maxChannels); if (resized) { consecutiveResizes++; @@ -342,8 +343,9 @@ 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. + // Log warning only once when the consecutive threshold is reached to avoid spamming logs. Log + // message will repeat if the number of consecutive resizes resets (e.g. stabilizes for a bit). + // However, aim to log once to ensure that this does not incur log spam. if (consecutiveResizes == CONSECUTIVE_RESIZE_THRESHOLD) { LOG.warning(CHANNEL_POOL_CONSECUTIVE_RESIZING_WARNING); } 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 504e0cce7b56..1019a2f0a345 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 @@ -59,7 +59,7 @@ public abstract class ChannelPoolSettings { static final Duration RESIZE_INTERVAL = Duration.ofMinutes(1); /** The maximum number of channels that can be added or removed at a time. */ - static final int MAX_RESIZE_DELTA = 2; + static final int DEFAULT_MAX_RESIZE_DELTA = 2; /** * Threshold to start scaling down the channel pool. @@ -96,8 +96,8 @@ 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 {@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. + * period. The default value is {@value #DEFAULT_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()}. */ @@ -127,11 +127,7 @@ boolean isStaticSize() { return true; } // When the scaling threshold are not set - if (getMinRpcsPerChannel() == 0 && getMaxRpcsPerChannel() == Integer.MAX_VALUE) { - return true; - } - - return false; + return getMinRpcsPerChannel() == 0 && getMaxRpcsPerChannel() == Integer.MAX_VALUE; } public abstract Builder toBuilder(); @@ -143,7 +139,6 @@ public static ChannelPoolSettings staticallySized(int size) { .setMaxRpcsPerChannel(Integer.MAX_VALUE) .setMinChannelCount(size) .setMaxChannelCount(size) - .setMaxResizeDelta(Math.min(MAX_RESIZE_DELTA, size)) .build(); } @@ -155,7 +150,7 @@ public static Builder builder() { .setMinRpcsPerChannel(0) .setMaxRpcsPerChannel(Integer.MAX_VALUE) .setPreemptiveRefreshEnabled(false) - .setMaxResizeDelta(MAX_RESIZE_DELTA); + .setMaxResizeDelta(DEFAULT_MAX_RESIZE_DELTA); } @AutoValue.Builder 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 1219d3679d92..950beb605406 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 @@ -784,9 +784,7 @@ void repeatedResizingLogsWarningOnExpand() throws Exception { 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."); + .contains(ChannelPool.CHANNEL_POOL_CONSECUTIVE_RESIZING_WARNING); // 6th resize, should not log again pool.resize(); @@ -837,9 +835,7 @@ void repeatedResizingLogsWarningOnShrink() throws Exception { // 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."); + .contains(ChannelPool.CHANNEL_POOL_CONSECUTIVE_RESIZING_WARNING); } finally { ChannelPool.LOG.removeHandler(logHandler); } From cbef704ef0b5e23998af9871db7a7d3a0c863b67 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 20 Apr 2026 12:48:09 -0400 Subject: [PATCH 15/22] feat(gax-grpc): remove maxResizeDelta validation and update javadoc --- .../java/com/google/api/gax/grpc/ChannelPoolSettings.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 1019a2f0a345..c19c6e27d78a 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 @@ -100,6 +100,11 @@ public abstract class ChannelPoolSettings { * 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()}. + * + *

    Note: This value can be greater than {@link #getMaxChannelCount()}. The resizing logic + * naturally clamps adjustments to never exceed the min or max channel bounds, so a larger delta + * simply means the pool can reach its required size in fewer steps without risk of exceeding + * bounds. */ public abstract int getMaxResizeDelta(); @@ -190,9 +195,6 @@ public ChannelPoolSettings build() { 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; } } From 2895af50ee97c25baec44d493fb4482e61c9ce51 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 20 Apr 2026 12:51:44 -0400 Subject: [PATCH 16/22] style(gax-grpc): update javadoc in ChannelPoolSettings to be a warning --- .../java/com/google/api/gax/grpc/ChannelPoolSettings.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 c19c6e27d78a..aa0065038cc2 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 @@ -101,10 +101,9 @@ public abstract class ChannelPoolSettings { * Regardless of this setting, the number of channels will never exceed {@link * #getMaxChannelCount()}. * - *

    Note: This value can be greater than {@link #getMaxChannelCount()}. The resizing logic - * naturally clamps adjustments to never exceed the min or max channel bounds, so a larger delta - * simply means the pool can reach its required size in fewer steps without risk of exceeding - * bounds. + *

    Warning: It is not recommended to configure this value to exceed {@link + * #getMaxChannelCount()}. While the resizing logic safely handles larger values by clamping + * adjustments to the bounds, setting it excessively high may obscure configuration intent. */ public abstract int getMaxResizeDelta(); From 068887ea728893564d2e256f6b22b9b673f72906 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 20 Apr 2026 12:55:46 -0400 Subject: [PATCH 17/22] feat(gax-grpc): restore maxResizeDelta validation and javadoc --- .../java/com/google/api/gax/grpc/ChannelPoolSettings.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 aa0065038cc2..2b31e274eada 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 @@ -100,10 +100,6 @@ public abstract class ChannelPoolSettings { * 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()}. - * - *

    Warning: It is not recommended to configure this value to exceed {@link - * #getMaxChannelCount()}. While the resizing logic safely handles larger values by clamping - * adjustments to the bounds, setting it excessively high may obscure configuration intent. */ public abstract int getMaxResizeDelta(); @@ -143,6 +139,7 @@ public static ChannelPoolSettings staticallySized(int size) { .setMaxRpcsPerChannel(Integer.MAX_VALUE) .setMinChannelCount(size) .setMaxChannelCount(size) + .setMaxResizeDelta(Math.min(DEFAULT_MAX_RESIZE_DELTA, size)) .build(); } @@ -194,6 +191,9 @@ public ChannelPoolSettings build() { 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; } } From 947fad51ccd6ffedbd9f60cb803f32dae47735af Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 20 Apr 2026 12:57:50 -0400 Subject: [PATCH 18/22] docs(gax-grpc): add comment explaining resize delta clamping in staticallySized --- .../main/java/com/google/api/gax/grpc/ChannelPoolSettings.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/ChannelPoolSettings.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java index 2b31e274eada..9e844318533a 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 @@ -139,6 +139,9 @@ public static ChannelPoolSettings staticallySized(int size) { .setMaxRpcsPerChannel(Integer.MAX_VALUE) .setMinChannelCount(size) .setMaxChannelCount(size) + // Maintain backwards compatibility by using default delta (2). + // For static size 1, clamp to avoid validation failure (delta <= max channels). + // Since static pools don't resize, this value doesn't affect operation. .setMaxResizeDelta(Math.min(DEFAULT_MAX_RESIZE_DELTA, size)) .build(); } From f62aa0c24c645315b520e92950efe6bf9be39894 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 20 Apr 2026 13:05:08 -0400 Subject: [PATCH 19/22] chore: Add a comment to explain the resize delta logic in static size --- .../java/com/google/api/gax/grpc/ChannelPoolSettings.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 9e844318533a..dc77f303541b 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 @@ -139,9 +139,8 @@ public static ChannelPoolSettings staticallySized(int size) { .setMaxRpcsPerChannel(Integer.MAX_VALUE) .setMinChannelCount(size) .setMaxChannelCount(size) - // Maintain backwards compatibility by using default delta (2). - // For static size 1, clamp to avoid validation failure (delta <= max channels). - // Since static pools don't resize, this value doesn't affect operation. + // Static pools don't resize so this value doesn't affect operation. However, + // validation still checks that resize delta doesn't exceed channel pool size. .setMaxResizeDelta(Math.min(DEFAULT_MAX_RESIZE_DELTA, size)) .build(); } From 697c336500da7adccf845f2078af5fabf7d66971 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 20 Apr 2026 18:52:32 -0400 Subject: [PATCH 20/22] feat(gax-grpc): add cap of 25 to maxResizeDelta and test --- .../com/google/api/gax/grpc/ChannelPoolSettings.java | 9 +++++++++ .../java/com/google/api/gax/grpc/ChannelPoolTest.java | 8 ++++++++ 2 files changed, 17 insertions(+) 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 dc77f303541b..248fc3c3d00e 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 @@ -61,6 +61,10 @@ public abstract class ChannelPoolSettings { /** The maximum number of channels that can be added or removed at a time. */ static final int DEFAULT_MAX_RESIZE_DELTA = 2; + // Arbitrary limit to prevent unbounded growth and protect server/client resources. + // Capping at 25 ensures we don't scale too aggressively in a single cycle. + private static final int MAX_ALLOWED_RESIZE_DELTA = 25; + /** * Threshold to start scaling down the channel pool. * @@ -100,6 +104,8 @@ public abstract class ChannelPoolSettings { * 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()}. + * + *

    Note: This value cannot exceed {@value #MAX_ALLOWED_RESIZE_DELTA}. */ public abstract int getMaxResizeDelta(); @@ -193,6 +199,9 @@ public ChannelPoolSettings build() { 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() <= MAX_ALLOWED_RESIZE_DELTA, + "Max resize delta cannot be greater than " + MAX_ALLOWED_RESIZE_DELTA); Preconditions.checkState( s.getMaxResizeDelta() <= s.getMaxChannelCount(), "Max resize delta cannot be greater than max channel count"); 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 950beb605406..b8c533ad3c82 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 @@ -899,4 +899,12 @@ void testDoubleRelease() throws Exception { ChannelPool.LOG.removeHandler(logHandler); } } + + @Test + void settingsValidationFailsWhenMaxResizeDeltaExceedsLimit() { + ChannelPoolSettings.Builder builder = + ChannelPoolSettings.builder().setMaxResizeDelta(26).setMaxChannelCount(30); + org.junit.jupiter.api.Assertions.assertThrows( + IllegalStateException.class, () -> builder.build()); + } } From 387c68e75ff425b2f182ae5471fe4a9bb693aac6 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 20 Apr 2026 18:58:41 -0400 Subject: [PATCH 21/22] docs(gax-grpc): add warning about high resize delta values in javadoc --- .../main/java/com/google/api/gax/grpc/ChannelPoolSettings.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/ChannelPoolSettings.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ChannelPoolSettings.java index 248fc3c3d00e..b6765e3240bf 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 @@ -106,6 +106,9 @@ public abstract class ChannelPoolSettings { * #getMaxChannelCount()}. * *

    Note: This value cannot exceed {@value #MAX_ALLOWED_RESIZE_DELTA}. + * + *

    Warning: Higher values for resize delta may still result in performance degradation + * during spikes due to rapid scaling. */ public abstract int getMaxResizeDelta(); From defe1bbcb4f97f708f44047ee3b50569b3bb824b Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 20 Apr 2026 18:59:31 -0400 Subject: [PATCH 22/22] docs(gax-grpc): add warning about high resize delta values to setter in javadoc --- .../java/com/google/api/gax/grpc/ChannelPoolSettings.java | 7 +++++++ 1 file changed, 7 insertions(+) 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 b6765e3240bf..31204ead57b6 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 @@ -179,6 +179,13 @@ public abstract static class Builder { public abstract Builder setPreemptiveRefreshEnabled(boolean enabled); + /** + * Sets the maximum number of channels that can be added or removed in a single resize cycle. + * This acts as a rate limiter to prevent wild fluctuations. + * + *

    Warning: Higher values for resize delta may still result in performance degradation + * during spikes due to rapid scaling. + */ public abstract Builder setMaxResizeDelta(int count); abstract ChannelPoolSettings autoBuild();