@@ -153,6 +153,16 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
153153 private final Duration partitionedDmlTimeout ;
154154 private final boolean grpcGcpExtensionEnabled ;
155155 private final GcpManagedChannelOptions grpcGcpOptions ;
156+ // Whether dynamic channel pooling is enabled (via automatic gRPC-GCP enablement) by default.
157+ // This is derived from the builder flag at build time.
158+ private final boolean dynamicChannelPoolEnabled ;
159+ // Dynamic Channel Pool parameters
160+ private final Integer dcpMaxRpcPerChannel ;
161+ private final Integer dcpMinRpcPerChannel ;
162+ private final Duration dcpScaleDownInterval ;
163+ private final Integer dcpInitialSize ;
164+ private final Integer dcpMaxChannels ;
165+ private final Integer dcpMinChannels ;
156166 private final boolean autoThrottleAdministrativeRequests ;
157167 private final RetrySettings retryAdministrativeRequestsSettings ;
158168 private final boolean trackTransactionStarter ;
@@ -800,6 +810,13 @@ protected SpannerOptions(Builder builder) {
800810 partitionedDmlTimeout = builder .partitionedDmlTimeout ;
801811 grpcGcpExtensionEnabled = builder .grpcGcpExtensionEnabled ;
802812 grpcGcpOptions = builder .grpcGcpOptions ;
813+ dynamicChannelPoolEnabled = builder .dynamicChannelPoolEnabled ;
814+ dcpMaxRpcPerChannel = builder .dcpMaxRpcPerChannel ;
815+ dcpMinRpcPerChannel = builder .dcpMinRpcPerChannel ;
816+ dcpScaleDownInterval = builder .dcpScaleDownInterval ;
817+ dcpInitialSize = builder .dcpInitialSize ;
818+ dcpMaxChannels = builder .dcpMaxChannels ;
819+ dcpMinChannels = builder .dcpMinChannels ;
803820 autoThrottleAdministrativeRequests = builder .autoThrottleAdministrativeRequests ;
804821 retryAdministrativeRequestsSettings = builder .retryAdministrativeRequestsSettings ;
805822 trackTransactionStarter = builder .trackTransactionStarter ;
@@ -1027,6 +1044,10 @@ public static class Builder
10271044 private Duration partitionedDmlTimeout = Duration .ofHours (2L );
10281045 private boolean grpcGcpExtensionEnabled = false ;
10291046 private GcpManagedChannelOptions grpcGcpOptions ;
1047+ // Tracks whether enable/disableGrpcGcpExtension has been explicitly called by the user.
1048+ private boolean grpcGcpExtensionExplicitlySet = false ;
1049+ // Dynamic Channel Pool (DCP) toggle. Default: enabled.
1050+ private boolean dynamicChannelPoolEnabled = true ;
10301051 private RetrySettings retryAdministrativeRequestsSettings =
10311052 DEFAULT_ADMIN_REQUESTS_LIMIT_EXCEEDED_RETRY_SETTINGS ;
10321053 private boolean autoThrottleAdministrativeRequests = false ;
@@ -1050,6 +1071,14 @@ public static class Builder
10501071 private boolean isExperimentalHost = false ;
10511072 private TransactionOptions defaultTransactionOptions = TransactionOptions .getDefaultInstance ();
10521073
1074+ // Dynamic Channel Pool configuration (defaults per dynamic_cahnnel_pooling.md)
1075+ private Integer dcpMaxRpcPerChannel = 25 ;
1076+ private Integer dcpMinRpcPerChannel = 15 ;
1077+ private Duration dcpScaleDownInterval = Duration .ofMinutes (3 );
1078+ private Integer dcpInitialSize = 4 ;
1079+ private Integer dcpMaxChannels = 10 ;
1080+ private Integer dcpMinChannels = 2 ;
1081+
10531082 private static String createCustomClientLibToken (String token ) {
10541083 return token + " " + ServiceOptions .getGoogApiClientLibName ();
10551084 }
@@ -1557,30 +1586,87 @@ public Builder setExperimentalHost(String host) {
15571586 return this ;
15581587 }
15591588
1560- /**
1561- * Enables gRPC-GCP extension with the default settings. Do not set
1562- * GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS to true in combination with this option, as
1563- * Multiplexed sessions are not supported for gRPC-GCP.
1564- */
1589+ /** Enables gRPC-GCP extension with the default settings. */
15651590 public Builder enableGrpcGcpExtension () {
15661591 return this .enableGrpcGcpExtension (null );
15671592 }
15681593
15691594 /**
15701595 * Enables gRPC-GCP extension and uses provided options for configuration. The metric registry
1571- * and default Spanner metric labels will be added automatically. Do not set
1572- * GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS to true in combination with this option, as
1573- * Multiplexed sessions are not supported for gRPC-GCP.
1596+ * and default Spanner metric labels will be added automatically.
15741597 */
15751598 public Builder enableGrpcGcpExtension (GcpManagedChannelOptions options ) {
15761599 this .grpcGcpExtensionEnabled = true ;
15771600 this .grpcGcpOptions = options ;
1601+ this .grpcGcpExtensionExplicitlySet = true ;
15781602 return this ;
15791603 }
15801604
15811605 /** Disables gRPC-GCP extension. */
15821606 public Builder disableGrpcGcpExtension () {
15831607 this .grpcGcpExtensionEnabled = false ;
1608+ this .grpcGcpExtensionExplicitlySet = true ;
1609+ return this ;
1610+ }
1611+
1612+ /**
1613+ * Enables or disables dynamic channel pooling. When enabled and no explicit number of channels
1614+ * has been configured and no custom {@link TransportChannelProvider} has been set, the client
1615+ * will automatically enable the gRPC-GCP channel pool. If multiplexed sessions are enabled,
1616+ * dynamic channel pooling will not be enabled.
1617+ */
1618+ public Builder setDynamicChannelPoolEnabled (boolean enabled ) {
1619+ this .dynamicChannelPoolEnabled = enabled ;
1620+ return this ;
1621+ }
1622+
1623+ // Granular DCP configuration setters with validation bounds
1624+ public Builder setDynamicPoolMaxRpc (int maxRpcPerChannel ) {
1625+ Preconditions .checkArgument (maxRpcPerChannel >= 1 && maxRpcPerChannel <= 100 ,
1626+ "maxRpcPerChannel must be in [1, 100]" );
1627+ this .dcpMaxRpcPerChannel = maxRpcPerChannel ;
1628+ return this ;
1629+ }
1630+
1631+ public Builder setDynamicPoolMinRpc (int minRpcPerChannel ) {
1632+ Preconditions .checkArgument (minRpcPerChannel >= 1 ,
1633+ "minRpcPerChannel must be >= 1" );
1634+ this .dcpMinRpcPerChannel = minRpcPerChannel ;
1635+ return this ;
1636+ }
1637+
1638+ public Builder setDynamicPoolScaleDownInterval (Duration interval ) {
1639+ Preconditions .checkNotNull (interval , "interval cannot be null" );
1640+ Preconditions .checkArgument (!interval .isNegative () && !interval .isZero (),
1641+ "interval must be > 0" );
1642+ Preconditions .checkArgument (
1643+ interval .compareTo (Duration .ofSeconds (30 )) >= 0 ,
1644+ "interval must be >= 30 seconds" );
1645+ Preconditions .checkArgument (
1646+ interval .compareTo (Duration .ofMinutes (60 )) <= 0 ,
1647+ "interval must be <= 60 minutes" );
1648+ this .dcpScaleDownInterval = interval ;
1649+ return this ;
1650+ }
1651+
1652+ public Builder setDynamicPoolInitialSize (int initialSize ) {
1653+ Preconditions .checkArgument (initialSize >= 1 && initialSize <= 256 ,
1654+ "initialSize must be in [1, 256]" );
1655+ this .dcpInitialSize = initialSize ;
1656+ return this ;
1657+ }
1658+
1659+ public Builder setDynamicPoolMaxChannels (int maxChannels ) {
1660+ Preconditions .checkArgument (maxChannels >= 1 && maxChannels <= 256 ,
1661+ "maxChannels must be in [1, 256]" );
1662+ this .dcpMaxChannels = maxChannels ;
1663+ return this ;
1664+ }
1665+
1666+ public Builder setDynamicPoolMinChannels (int minChannels ) {
1667+ Preconditions .checkArgument (minChannels >= 1 ,
1668+ "minChannels must be >= 1" );
1669+ this .dcpMinChannels = minChannels ;
15841670 return this ;
15851671 }
15861672
@@ -1792,6 +1878,15 @@ public SpannerOptions build() {
17921878 } else if (isExperimentalHost && credentials == null ) {
17931879 credentials = environment .getDefaultExperimentalHostCredentials ();
17941880 }
1881+ // Auto-enable gRPC-GCP (dynamic channel pool) if allowed and not explicitly overridden.
1882+ if (!grpcGcpExtensionExplicitlySet && dynamicChannelPoolEnabled ) {
1883+ boolean hasCustomChannelProvider = this .channelProvider != null ;
1884+ boolean hasStaticNumChannels = this .numChannels != null ;
1885+ if (!hasCustomChannelProvider && !hasStaticNumChannels ) {
1886+ this .grpcGcpExtensionEnabled = true ;
1887+ }
1888+ }
1889+
17951890 if (this .numChannels == null ) {
17961891 this .numChannels =
17971892 this .grpcGcpExtensionEnabled ? GRPC_GCP_ENABLED_DEFAULT_CHANNELS : DEFAULT_CHANNELS ;
@@ -1996,6 +2091,19 @@ public GcpManagedChannelOptions getGrpcGcpOptions() {
19962091 return grpcGcpOptions ;
19972092 }
19982093
2094+ /** Returns whether dynamic channel pooling is enabled by default. */
2095+ public boolean isDynamicChannelPoolEnabled () {
2096+ return dynamicChannelPoolEnabled ;
2097+ }
2098+
2099+ // Dynamic Channel Pool getters used by channel setup
2100+ public Integer getDcpMaxRpcPerChannel () { return dcpMaxRpcPerChannel ; }
2101+ public Integer getDcpMinRpcPerChannel () { return dcpMinRpcPerChannel ; }
2102+ public Duration getDcpScaleDownInterval () { return dcpScaleDownInterval ; }
2103+ public Integer getDcpInitialSize () { return dcpInitialSize ; }
2104+ public Integer getDcpMaxChannels () { return dcpMaxChannels ; }
2105+ public Integer getDcpMinChannels () { return dcpMinChannels ; }
2106+
19992107 public boolean isAutoThrottleAdministrativeRequests () {
20002108 return autoThrottleAdministrativeRequests ;
20012109 }
0 commit comments