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

Commit 97db5be

Browse files
committed
support setting GcpChannelPoolOptions directly
1 parent cf39158 commit 97db5be

3 files changed

Lines changed: 133 additions & 421 deletions

File tree

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

Lines changed: 70 additions & 255 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import com.google.cloud.ServiceRpc;
4444
import com.google.cloud.TransportOptions;
4545
import com.google.cloud.grpc.GcpManagedChannelOptions;
46+
import com.google.cloud.grpc.GcpManagedChannelOptions.GcpChannelPoolOptions;
4647
import com.google.cloud.grpc.GrpcTransportOptions;
4748
import com.google.cloud.spanner.Options.DirectedReadOption;
4849
import com.google.cloud.spanner.Options.QueryOption;
@@ -154,13 +155,6 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
154155
/** Default min number of channels for dynamic pool. */
155156
public static final int DEFAULT_DYNAMIC_POOL_MIN_CHANNELS = 2;
156157

157-
// DCP bounds
158-
static final int MAX_DYNAMIC_POOL_MAX_RPC = 100;
159-
static final int MAX_DYNAMIC_POOL_MAX_CHANNELS = 20;
160-
static final int MAX_DYNAMIC_POOL_INITIAL_SIZE = 256;
161-
static final Duration MIN_DYNAMIC_POOL_SCALE_DOWN_INTERVAL = Duration.ofSeconds(30);
162-
static final Duration MAX_DYNAMIC_POOL_SCALE_DOWN_INTERVAL = Duration.ofHours(1);
163-
164158
/**
165159
* Default affinity key lifetime for dynamic channel pool. This is how long to keep an affinity
166160
* key after its last use. Zero means keeping keys forever. Default is 10 minutes, which is
@@ -174,6 +168,39 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
174168
*/
175169
public static final Duration DEFAULT_DYNAMIC_POOL_CLEANUP_INTERVAL = Duration.ofMinutes(1);
176170

171+
/**
172+
* Creates a {@link GcpChannelPoolOptions} instance with Spanner-specific defaults for dynamic
173+
* channel pooling. These defaults are optimized for typical Spanner workloads.
174+
*
175+
* <p>Default values:
176+
*
177+
* <ul>
178+
* <li>Max size: {@value #DEFAULT_DYNAMIC_POOL_MAX_CHANNELS}
179+
* <li>Min size: {@value #DEFAULT_DYNAMIC_POOL_MIN_CHANNELS}
180+
* <li>Initial size: {@value #DEFAULT_DYNAMIC_POOL_INITIAL_SIZE}
181+
* <li>Max RPC per channel: {@value #DEFAULT_DYNAMIC_POOL_MAX_RPC}
182+
* <li>Min RPC per channel: {@value #DEFAULT_DYNAMIC_POOL_MIN_RPC}
183+
* <li>Scale down interval: 3 minutes
184+
* <li>Affinity key lifetime: 10 minutes
185+
* <li>Cleanup interval: 1 minute
186+
* </ul>
187+
*
188+
* @return a new {@link GcpChannelPoolOptions} instance with Spanner defaults
189+
*/
190+
public static GcpChannelPoolOptions createDefaultDynamicChannelPoolOptions() {
191+
return GcpChannelPoolOptions.newBuilder()
192+
.setMaxSize(DEFAULT_DYNAMIC_POOL_MAX_CHANNELS)
193+
.setMinSize(DEFAULT_DYNAMIC_POOL_MIN_CHANNELS)
194+
.setInitSize(DEFAULT_DYNAMIC_POOL_INITIAL_SIZE)
195+
.setDynamicScaling(
196+
DEFAULT_DYNAMIC_POOL_MIN_RPC,
197+
DEFAULT_DYNAMIC_POOL_MAX_RPC,
198+
DEFAULT_DYNAMIC_POOL_SCALE_DOWN_INTERVAL)
199+
.setAffinityKeyLifetime(DEFAULT_DYNAMIC_POOL_AFFINITY_KEY_LIFETIME)
200+
.setCleanupInterval(DEFAULT_DYNAMIC_POOL_CLEANUP_INTERVAL)
201+
.build();
202+
}
203+
177204
private final TransportChannelProvider channelProvider;
178205

179206
@SuppressWarnings("rawtypes")
@@ -194,14 +221,7 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
194221
private final boolean grpcGcpExtensionEnabled;
195222
private final GcpManagedChannelOptions grpcGcpOptions;
196223
private final boolean dynamicChannelPoolEnabled;
197-
private final int dynamicPoolMaxRpc;
198-
private final int dynamicPoolMinRpc;
199-
private final Duration dynamicPoolScaleDownInterval;
200-
private final int dynamicPoolInitialSize;
201-
private final int dynamicPoolMaxChannels;
202-
private final int dynamicPoolMinChannels;
203-
private final Duration dynamicPoolAffinityKeyLifetime;
204-
private final Duration dynamicPoolCleanupInterval;
224+
private final GcpChannelPoolOptions gcpChannelPoolOptions;
205225
private final boolean autoThrottleAdministrativeRequests;
206226
private final RetrySettings retryAdministrativeRequestsSettings;
207227
private final boolean trackTransactionStarter;
@@ -863,68 +883,11 @@ protected SpannerOptions(Builder builder) {
863883
dynamicChannelPoolEnabled = false;
864884
}
865885

866-
// Use defaults with proper bounds checking for DCP configuration
867-
int effectiveMaxRpc =
868-
builder.dynamicPoolMaxRpc != null
869-
? builder.dynamicPoolMaxRpc
870-
: DEFAULT_DYNAMIC_POOL_MAX_RPC;
871-
dynamicPoolMaxRpc = effectiveMaxRpc;
872-
873-
int effectiveMinRpc =
874-
builder.dynamicPoolMinRpc != null
875-
? builder.dynamicPoolMinRpc
876-
: DEFAULT_DYNAMIC_POOL_MIN_RPC;
877-
// Ensure minRpc does not exceed maxRpc
878-
dynamicPoolMinRpc = Math.min(effectiveMinRpc, effectiveMaxRpc);
879-
880-
dynamicPoolScaleDownInterval =
881-
builder.dynamicPoolScaleDownInterval != null
882-
? builder.dynamicPoolScaleDownInterval
883-
: DEFAULT_DYNAMIC_POOL_SCALE_DOWN_INTERVAL;
884-
885-
int effectiveMaxChannels =
886-
builder.dynamicPoolMaxChannels != null
887-
? builder.dynamicPoolMaxChannels
888-
: DEFAULT_DYNAMIC_POOL_MAX_CHANNELS;
889-
dynamicPoolMaxChannels = effectiveMaxChannels;
890-
891-
int effectiveMinChannels =
892-
builder.dynamicPoolMinChannels != null
893-
? builder.dynamicPoolMinChannels
894-
: DEFAULT_DYNAMIC_POOL_MIN_CHANNELS;
895-
// Ensure minChannels does not exceed maxChannels
896-
dynamicPoolMinChannels = Math.min(effectiveMinChannels, effectiveMaxChannels);
897-
898-
int effectiveInitialSize =
899-
builder.dynamicPoolInitialSize != null
900-
? builder.dynamicPoolInitialSize
901-
: DEFAULT_DYNAMIC_POOL_INITIAL_SIZE;
902-
// Ensure initialSize is within [minChannels, maxChannels]
903-
dynamicPoolInitialSize =
904-
Math.max(dynamicPoolMinChannels, Math.min(effectiveInitialSize, dynamicPoolMaxChannels));
905-
906-
dynamicPoolAffinityKeyLifetime =
907-
builder.dynamicPoolAffinityKeyLifetime != null
908-
? builder.dynamicPoolAffinityKeyLifetime
909-
: DEFAULT_DYNAMIC_POOL_AFFINITY_KEY_LIFETIME;
910-
911-
// If cleanup interval is not set but affinity key lifetime is set, default to 1/10 of lifetime
912-
if (builder.dynamicPoolCleanupInterval != null) {
913-
dynamicPoolCleanupInterval = builder.dynamicPoolCleanupInterval;
914-
} else if (!dynamicPoolAffinityKeyLifetime.isZero()) {
915-
dynamicPoolCleanupInterval = dynamicPoolAffinityKeyLifetime.dividedBy(10);
916-
} else {
917-
dynamicPoolCleanupInterval = DEFAULT_DYNAMIC_POOL_CLEANUP_INTERVAL;
918-
}
919-
920-
// Validate that if affinity key lifetime is set (non-zero), cleanup interval must be positive.
921-
// A zero cleanup interval with a non-zero affinity key lifetime would disable cleanup of stale
922-
// affinity keys, potentially leading to a memory leak.
923-
Preconditions.checkArgument(
924-
dynamicPoolAffinityKeyLifetime.isZero() || !dynamicPoolCleanupInterval.isZero(),
925-
"Cleanup interval must be positive when affinity key lifetime is set, got cleanup interval:"
926-
+ " %s",
927-
dynamicPoolCleanupInterval);
886+
// Use user-provided GcpChannelPoolOptions or create Spanner-specific defaults
887+
gcpChannelPoolOptions =
888+
builder.gcpChannelPoolOptions != null
889+
? builder.gcpChannelPoolOptions
890+
: createDefaultDynamicChannelPoolOptions();
928891

929892
autoThrottleAdministrativeRequests = builder.autoThrottleAdministrativeRequests;
930893
retryAdministrativeRequestsSettings = builder.retryAdministrativeRequestsSettings;
@@ -1155,14 +1118,7 @@ public static class Builder
11551118
private boolean grpcGcpExtensionEnabled = true;
11561119
private GcpManagedChannelOptions grpcGcpOptions;
11571120
private Boolean dynamicChannelPoolEnabled;
1158-
private Integer dynamicPoolMaxRpc;
1159-
private Integer dynamicPoolMinRpc;
1160-
private Duration dynamicPoolScaleDownInterval;
1161-
private Integer dynamicPoolInitialSize;
1162-
private Integer dynamicPoolMaxChannels;
1163-
private Integer dynamicPoolMinChannels;
1164-
private Duration dynamicPoolAffinityKeyLifetime;
1165-
private Duration dynamicPoolCleanupInterval;
1121+
private GcpChannelPoolOptions gcpChannelPoolOptions;
11661122
private RetrySettings retryAdministrativeRequestsSettings =
11671123
DEFAULT_ADMIN_REQUESTS_LIMIT_EXCEEDED_RETRY_SETTINGS;
11681124
private boolean autoThrottleAdministrativeRequests = false;
@@ -1236,14 +1192,7 @@ protected Builder() {
12361192
this.grpcGcpExtensionEnabled = options.grpcGcpExtensionEnabled;
12371193
this.grpcGcpOptions = options.grpcGcpOptions;
12381194
this.dynamicChannelPoolEnabled = options.dynamicChannelPoolEnabled;
1239-
this.dynamicPoolMaxRpc = options.dynamicPoolMaxRpc;
1240-
this.dynamicPoolMinRpc = options.dynamicPoolMinRpc;
1241-
this.dynamicPoolScaleDownInterval = options.dynamicPoolScaleDownInterval;
1242-
this.dynamicPoolInitialSize = options.dynamicPoolInitialSize;
1243-
this.dynamicPoolMaxChannels = options.dynamicPoolMaxChannels;
1244-
this.dynamicPoolMinChannels = options.dynamicPoolMinChannels;
1245-
this.dynamicPoolAffinityKeyLifetime = options.dynamicPoolAffinityKeyLifetime;
1246-
this.dynamicPoolCleanupInterval = options.dynamicPoolCleanupInterval;
1195+
this.gcpChannelPoolOptions = options.gcpChannelPoolOptions;
12471196
this.autoThrottleAdministrativeRequests = options.autoThrottleAdministrativeRequests;
12481197
this.retryAdministrativeRequestsSettings = options.retryAdministrativeRequestsSettings;
12491198
this.trackTransactionStarter = options.trackTransactionStarter;
@@ -1750,128 +1699,33 @@ public Builder disableDynamicChannelPool() {
17501699
}
17511700

17521701
/**
1753-
* Sets the maximum number of concurrent RPCs per channel before triggering a scale up of the
1754-
* channel pool. Default is 25. Must be between 1 and 100.
1755-
*
1756-
* <p>This setting is only effective when dynamic channel pooling is enabled.
1757-
*/
1758-
public Builder setDynamicPoolMaxRpc(int maxRpc) {
1759-
Preconditions.checkArgument(
1760-
maxRpc >= 1 && maxRpc <= MAX_DYNAMIC_POOL_MAX_RPC,
1761-
"Dynamic pool max RPC must be between 1 and %s, got: %s",
1762-
MAX_DYNAMIC_POOL_MAX_RPC,
1763-
maxRpc);
1764-
this.dynamicPoolMaxRpc = maxRpc;
1765-
return this;
1766-
}
1767-
1768-
/**
1769-
* Sets the minimum number of concurrent RPCs per channel used for scale down checks. When the
1770-
* average concurrent RPCs per channel falls below this value, the pool may scale down. Default
1771-
* is 15. Must be between 1 and the configured max RPC value.
1772-
*
1773-
* <p>This setting is only effective when dynamic channel pooling is enabled.
1774-
*/
1775-
public Builder setDynamicPoolMinRpc(int minRpc) {
1776-
Preconditions.checkArgument(
1777-
minRpc >= 1, "Dynamic pool min RPC must be at least 1, got: %s", minRpc);
1778-
this.dynamicPoolMinRpc = minRpc;
1779-
return this;
1780-
}
1781-
1782-
/**
1783-
* Sets the interval at which the channel pool checks whether it can scale down. Default is 3
1784-
* minutes. Must be between 30 seconds and 1 hour.
1785-
*
1786-
* <p>This setting is only effective when dynamic channel pooling is enabled.
1787-
*/
1788-
public Builder setDynamicPoolScaleDownInterval(Duration interval) {
1789-
Preconditions.checkNotNull(interval, "Scale down interval cannot be null");
1790-
Preconditions.checkArgument(
1791-
interval.compareTo(MIN_DYNAMIC_POOL_SCALE_DOWN_INTERVAL) >= 0
1792-
&& interval.compareTo(MAX_DYNAMIC_POOL_SCALE_DOWN_INTERVAL) <= 0,
1793-
"Scale down interval must be between %s and %s, got: %s",
1794-
MIN_DYNAMIC_POOL_SCALE_DOWN_INTERVAL,
1795-
MAX_DYNAMIC_POOL_SCALE_DOWN_INTERVAL,
1796-
interval);
1797-
this.dynamicPoolScaleDownInterval = interval;
1798-
return this;
1799-
}
1800-
1801-
/**
1802-
* Sets the initial number of channels to create for the dynamic channel pool. Default is 4.
1803-
* Must be between 1 and 256.
1804-
*
1805-
* <p>This setting is only effective when dynamic channel pooling is enabled.
1806-
*/
1807-
public Builder setDynamicPoolInitialSize(int initialSize) {
1808-
Preconditions.checkArgument(
1809-
initialSize >= 1 && initialSize <= MAX_DYNAMIC_POOL_INITIAL_SIZE,
1810-
"Dynamic pool initial size must be between 1 and %s, got: %s",
1811-
MAX_DYNAMIC_POOL_INITIAL_SIZE,
1812-
initialSize);
1813-
this.dynamicPoolInitialSize = initialSize;
1814-
return this;
1815-
}
1816-
1817-
/**
1818-
* Sets the maximum number of channels for the dynamic channel pool. Default is 10. Must be
1819-
* between 1 and 20.
1702+
* Sets the channel pool options for dynamic channel pooling. Use this to configure the dynamic
1703+
* channel pool behavior when {@link #enableDynamicChannelPool()} is enabled.
18201704
*
1821-
* <p>This setting is only effective when dynamic channel pooling is enabled.
1822-
*/
1823-
public Builder setDynamicPoolMaxChannels(int maxChannels) {
1824-
Preconditions.checkArgument(
1825-
maxChannels >= 1 && maxChannels <= MAX_DYNAMIC_POOL_MAX_CHANNELS,
1826-
"Dynamic pool max channels must be between 1 and %s, got: %s",
1827-
MAX_DYNAMIC_POOL_MAX_CHANNELS,
1828-
maxChannels);
1829-
this.dynamicPoolMaxChannels = maxChannels;
1830-
return this;
1831-
}
1832-
1833-
/**
1834-
* Sets the minimum number of channels for the dynamic channel pool. The pool will not scale
1835-
* down below this number. Default is 2. Must be between 1 and the configured max channels.
1705+
* <p>If not set, Spanner-specific defaults will be used (see {@link
1706+
* #createDefaultDynamicChannelPoolOptions()}).
18361707
*
1837-
* <p>This setting is only effective when dynamic channel pooling is enabled.
1838-
*/
1839-
public Builder setDynamicPoolMinChannels(int minChannels) {
1840-
Preconditions.checkArgument(
1841-
minChannels >= 1, "Dynamic pool min channels must be at least 1, got: %s", minChannels);
1842-
this.dynamicPoolMinChannels = minChannels;
1843-
return this;
1844-
}
1845-
1846-
/**
1847-
* Sets the affinity key lifetime for the dynamic channel pool. This determines how long to keep
1848-
* an affinity key after its last use. Setting this to a non-zero value enables automatic
1849-
* cleanup of stale affinity keys, which is important for long-running applications. Default is
1850-
* 1 hour. Use {@link Duration#ZERO} to keep keys forever (not recommended for long-running
1851-
* applications).
1708+
* <p>Example usage:
18521709
*
1853-
* <p>This setting is only effective when dynamic channel pooling is enabled.
1854-
*/
1855-
public Builder setDynamicPoolAffinityKeyLifetime(Duration lifetime) {
1856-
Preconditions.checkNotNull(lifetime, "Affinity key lifetime cannot be null");
1857-
Preconditions.checkArgument(
1858-
!lifetime.isNegative(), "Affinity key lifetime must not be negative, got: %s", lifetime);
1859-
this.dynamicPoolAffinityKeyLifetime = lifetime;
1860-
return this;
1861-
}
1862-
1863-
/**
1864-
* Sets the cleanup interval for the dynamic channel pool affinity keys. This determines how
1865-
* frequently the affinity key cleanup process runs. Default is 1 minute. Must be positive if
1866-
* affinity key lifetime is set.
1710+
* <pre>{@code
1711+
* SpannerOptions options = SpannerOptions.newBuilder()
1712+
* .setProjectId("my-project")
1713+
* .enableDynamicChannelPool()
1714+
* .setGcpChannelPoolOptions(
1715+
* GcpChannelPoolOptions.newBuilder()
1716+
* .setMaxSize(15)
1717+
* .setMinSize(3)
1718+
* .setInitSize(5)
1719+
* .setDynamicScaling(10, 30, Duration.ofMinutes(5))
1720+
* .build())
1721+
* .build();
1722+
* }</pre>
18671723
*
1868-
* <p>This setting is only effective when dynamic channel pooling is enabled.
1724+
* @param gcpChannelPoolOptions the channel pool options to use
1725+
* @return this builder for chaining
18691726
*/
1870-
public Builder setDynamicPoolCleanupInterval(Duration interval) {
1871-
Preconditions.checkNotNull(interval, "Cleanup interval cannot be null");
1872-
Preconditions.checkArgument(
1873-
!interval.isNegative(), "Cleanup interval must not be negative, got: %s", interval);
1874-
this.dynamicPoolCleanupInterval = interval;
1727+
public Builder setGcpChannelPoolOptions(GcpChannelPoolOptions gcpChannelPoolOptions) {
1728+
this.gcpChannelPoolOptions = Preconditions.checkNotNull(gcpChannelPoolOptions);
18751729
return this;
18761730
}
18771731

@@ -2298,52 +2152,13 @@ public boolean isDynamicChannelPoolEnabled() {
22982152
}
22992153

23002154
/**
2301-
* Returns the maximum number of concurrent RPCs per channel before triggering a scale up. Default
2302-
* is 25.
2303-
*/
2304-
public int getDynamicPoolMaxRpc() {
2305-
return dynamicPoolMaxRpc;
2306-
}
2307-
2308-
/**
2309-
* Returns the minimum number of concurrent RPCs per channel used for scale down checks. Default
2310-
* is 15.
2311-
*/
2312-
public int getDynamicPoolMinRpc() {
2313-
return dynamicPoolMinRpc;
2314-
}
2315-
2316-
/** Returns the scale down check interval. Default is 3 minutes. */
2317-
public Duration getDynamicPoolScaleDownInterval() {
2318-
return dynamicPoolScaleDownInterval;
2319-
}
2320-
2321-
/** Returns the initial number of channels for the dynamic pool. Default is 4. */
2322-
public int getDynamicPoolInitialSize() {
2323-
return dynamicPoolInitialSize;
2324-
}
2325-
2326-
/** Returns the maximum number of channels for the dynamic pool. Default is 10. */
2327-
public int getDynamicPoolMaxChannels() {
2328-
return dynamicPoolMaxChannels;
2329-
}
2330-
2331-
/** Returns the minimum number of channels for the dynamic pool. Default is 2. */
2332-
public int getDynamicPoolMinChannels() {
2333-
return dynamicPoolMinChannels;
2334-
}
2335-
2336-
/**
2337-
* Returns the affinity key lifetime for the dynamic pool. This is how long to keep an affinity
2338-
* key after its last use. Default is 10 minutes.
2155+
* Returns the channel pool options for dynamic channel pooling. If no options were explicitly
2156+
* set, returns the Spanner-specific defaults.
2157+
*
2158+
* @see #createDefaultDynamicChannelPoolOptions()
23392159
*/
2340-
public Duration getDynamicPoolAffinityKeyLifetime() {
2341-
return dynamicPoolAffinityKeyLifetime;
2342-
}
2343-
2344-
/** Returns the cleanup interval for dynamic pool affinity keys. Default is 1 minute. */
2345-
public Duration getDynamicPoolCleanupInterval() {
2346-
return dynamicPoolCleanupInterval;
2160+
public GcpChannelPoolOptions getGcpChannelPoolOptions() {
2161+
return gcpChannelPoolOptions;
23472162
}
23482163

23492164
public boolean isAutoThrottleAdministrativeRequests() {

0 commit comments

Comments
 (0)