@@ -151,6 +151,16 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
151151 private final Duration partitionedDmlTimeout ;
152152 private final boolean grpcGcpExtensionEnabled ;
153153 private final GcpManagedChannelOptions grpcGcpOptions ;
154+ // Whether dynamic channel pooling is enabled (via automatic gRPC-GCP enablement) by default.
155+ // This is derived from the builder flag at build time.
156+ private final boolean dynamicChannelPoolEnabled ;
157+ // Dynamic Channel Pool parameters
158+ private final Integer dcpMaxRpcPerChannel ;
159+ private final Integer dcpMinRpcPerChannel ;
160+ private final Duration dcpScaleDownInterval ;
161+ private final Integer dcpInitialSize ;
162+ private final Integer dcpMaxChannels ;
163+ private final Integer dcpMinChannels ;
154164 private final boolean autoThrottleAdministrativeRequests ;
155165 private final RetrySettings retryAdministrativeRequestsSettings ;
156166 private final boolean trackTransactionStarter ;
@@ -788,6 +798,13 @@ protected SpannerOptions(Builder builder) {
788798 partitionedDmlTimeout = builder .partitionedDmlTimeout ;
789799 grpcGcpExtensionEnabled = builder .grpcGcpExtensionEnabled ;
790800 grpcGcpOptions = builder .grpcGcpOptions ;
801+ dynamicChannelPoolEnabled = builder .dynamicChannelPoolEnabled ;
802+ dcpMaxRpcPerChannel = builder .dcpMaxRpcPerChannel ;
803+ dcpMinRpcPerChannel = builder .dcpMinRpcPerChannel ;
804+ dcpScaleDownInterval = builder .dcpScaleDownInterval ;
805+ dcpInitialSize = builder .dcpInitialSize ;
806+ dcpMaxChannels = builder .dcpMaxChannels ;
807+ dcpMinChannels = builder .dcpMinChannels ;
791808 autoThrottleAdministrativeRequests = builder .autoThrottleAdministrativeRequests ;
792809 retryAdministrativeRequestsSettings = builder .retryAdministrativeRequestsSettings ;
793810 trackTransactionStarter = builder .trackTransactionStarter ;
@@ -1002,6 +1019,10 @@ public static class Builder
10021019 private Duration partitionedDmlTimeout = Duration .ofHours (2L );
10031020 private boolean grpcGcpExtensionEnabled = false ;
10041021 private GcpManagedChannelOptions grpcGcpOptions ;
1022+ // Tracks whether enable/disableGrpcGcpExtension has been explicitly called by the user.
1023+ private boolean grpcGcpExtensionExplicitlySet = false ;
1024+ // Dynamic Channel Pool (DCP) toggle. Default: enabled.
1025+ private boolean dynamicChannelPoolEnabled = true ;
10051026 private RetrySettings retryAdministrativeRequestsSettings =
10061027 DEFAULT_ADMIN_REQUESTS_LIMIT_EXCEEDED_RETRY_SETTINGS ;
10071028 private boolean autoThrottleAdministrativeRequests = false ;
@@ -1025,6 +1046,14 @@ public static class Builder
10251046 private boolean isExperimentalHost = false ;
10261047 private TransactionOptions defaultTransactionOptions = TransactionOptions .getDefaultInstance ();
10271048
1049+ // Dynamic Channel Pool configuration (defaults per dynamic_cahnnel_pooling.md)
1050+ private Integer dcpMaxRpcPerChannel = 25 ;
1051+ private Integer dcpMinRpcPerChannel = 15 ;
1052+ private Duration dcpScaleDownInterval = Duration .ofMinutes (3 );
1053+ private Integer dcpInitialSize = 4 ;
1054+ private Integer dcpMaxChannels = 10 ;
1055+ private Integer dcpMinChannels = 2 ;
1056+
10281057 private static String createCustomClientLibToken (String token ) {
10291058 return token + " " + ServiceOptions .getGoogApiClientLibName ();
10301059 }
@@ -1532,30 +1561,87 @@ public Builder setExperimentalHost(String host) {
15321561 return this ;
15331562 }
15341563
1535- /**
1536- * Enables gRPC-GCP extension with the default settings. Do not set
1537- * GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS to true in combination with this option, as
1538- * Multiplexed sessions are not supported for gRPC-GCP.
1539- */
1564+ /** Enables gRPC-GCP extension with the default settings. */
15401565 public Builder enableGrpcGcpExtension () {
15411566 return this .enableGrpcGcpExtension (null );
15421567 }
15431568
15441569 /**
15451570 * Enables gRPC-GCP extension and uses provided options for configuration. The metric registry
1546- * and default Spanner metric labels will be added automatically. Do not set
1547- * GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS to true in combination with this option, as
1548- * Multiplexed sessions are not supported for gRPC-GCP.
1571+ * and default Spanner metric labels will be added automatically.
15491572 */
15501573 public Builder enableGrpcGcpExtension (GcpManagedChannelOptions options ) {
15511574 this .grpcGcpExtensionEnabled = true ;
15521575 this .grpcGcpOptions = options ;
1576+ this .grpcGcpExtensionExplicitlySet = true ;
15531577 return this ;
15541578 }
15551579
15561580 /** Disables gRPC-GCP extension. */
15571581 public Builder disableGrpcGcpExtension () {
15581582 this .grpcGcpExtensionEnabled = false ;
1583+ this .grpcGcpExtensionExplicitlySet = true ;
1584+ return this ;
1585+ }
1586+
1587+ /**
1588+ * Enables or disables dynamic channel pooling. When enabled and no explicit number of channels
1589+ * has been configured and no custom {@link TransportChannelProvider} has been set, the client
1590+ * will automatically enable the gRPC-GCP channel pool. If multiplexed sessions are enabled,
1591+ * dynamic channel pooling will not be enabled.
1592+ */
1593+ public Builder setDynamicChannelPoolEnabled (boolean enabled ) {
1594+ this .dynamicChannelPoolEnabled = enabled ;
1595+ return this ;
1596+ }
1597+
1598+ // Granular DCP configuration setters with validation bounds
1599+ public Builder setDynamicPoolMaxRpc (int maxRpcPerChannel ) {
1600+ Preconditions .checkArgument (maxRpcPerChannel >= 1 && maxRpcPerChannel <= 100 ,
1601+ "maxRpcPerChannel must be in [1, 100]" );
1602+ this .dcpMaxRpcPerChannel = maxRpcPerChannel ;
1603+ return this ;
1604+ }
1605+
1606+ public Builder setDynamicPoolMinRpc (int minRpcPerChannel ) {
1607+ Preconditions .checkArgument (minRpcPerChannel >= 1 ,
1608+ "minRpcPerChannel must be >= 1" );
1609+ this .dcpMinRpcPerChannel = minRpcPerChannel ;
1610+ return this ;
1611+ }
1612+
1613+ public Builder setDynamicPoolScaleDownInterval (Duration interval ) {
1614+ Preconditions .checkNotNull (interval , "interval cannot be null" );
1615+ Preconditions .checkArgument (!interval .isNegative () && !interval .isZero (),
1616+ "interval must be > 0" );
1617+ Preconditions .checkArgument (
1618+ interval .compareTo (Duration .ofSeconds (30 )) >= 0 ,
1619+ "interval must be >= 30 seconds" );
1620+ Preconditions .checkArgument (
1621+ interval .compareTo (Duration .ofMinutes (60 )) <= 0 ,
1622+ "interval must be <= 60 minutes" );
1623+ this .dcpScaleDownInterval = interval ;
1624+ return this ;
1625+ }
1626+
1627+ public Builder setDynamicPoolInitialSize (int initialSize ) {
1628+ Preconditions .checkArgument (initialSize >= 1 && initialSize <= 256 ,
1629+ "initialSize must be in [1, 256]" );
1630+ this .dcpInitialSize = initialSize ;
1631+ return this ;
1632+ }
1633+
1634+ public Builder setDynamicPoolMaxChannels (int maxChannels ) {
1635+ Preconditions .checkArgument (maxChannels >= 1 && maxChannels <= 256 ,
1636+ "maxChannels must be in [1, 256]" );
1637+ this .dcpMaxChannels = maxChannels ;
1638+ return this ;
1639+ }
1640+
1641+ public Builder setDynamicPoolMinChannels (int minChannels ) {
1642+ Preconditions .checkArgument (minChannels >= 1 ,
1643+ "minChannels must be >= 1" );
1644+ this .dcpMinChannels = minChannels ;
15591645 return this ;
15601646 }
15611647
@@ -1756,6 +1842,15 @@ public SpannerOptions build() {
17561842 } else if (isExperimentalHost && credentials == null ) {
17571843 credentials = environment .getDefaultExperimentalHostCredentials ();
17581844 }
1845+ // Auto-enable gRPC-GCP (dynamic channel pool) if allowed and not explicitly overridden.
1846+ if (!grpcGcpExtensionExplicitlySet && dynamicChannelPoolEnabled ) {
1847+ boolean hasCustomChannelProvider = this .channelProvider != null ;
1848+ boolean hasStaticNumChannels = this .numChannels != null ;
1849+ if (!hasCustomChannelProvider && !hasStaticNumChannels ) {
1850+ this .grpcGcpExtensionEnabled = true ;
1851+ }
1852+ }
1853+
17591854 if (this .numChannels == null ) {
17601855 this .numChannels =
17611856 this .grpcGcpExtensionEnabled ? GRPC_GCP_ENABLED_DEFAULT_CHANNELS : DEFAULT_CHANNELS ;
@@ -1960,6 +2055,19 @@ public GcpManagedChannelOptions getGrpcGcpOptions() {
19602055 return grpcGcpOptions ;
19612056 }
19622057
2058+ /** Returns whether dynamic channel pooling is enabled by default. */
2059+ public boolean isDynamicChannelPoolEnabled () {
2060+ return dynamicChannelPoolEnabled ;
2061+ }
2062+
2063+ // Dynamic Channel Pool getters used by channel setup
2064+ public Integer getDcpMaxRpcPerChannel () { return dcpMaxRpcPerChannel ; }
2065+ public Integer getDcpMinRpcPerChannel () { return dcpMinRpcPerChannel ; }
2066+ public Duration getDcpScaleDownInterval () { return dcpScaleDownInterval ; }
2067+ public Integer getDcpInitialSize () { return dcpInitialSize ; }
2068+ public Integer getDcpMaxChannels () { return dcpMaxChannels ; }
2069+ public Integer getDcpMinChannels () { return dcpMinChannels ; }
2070+
19632071 public boolean isAutoThrottleAdministrativeRequests () {
19642072 return autoThrottleAdministrativeRequests ;
19652073 }
0 commit comments