@@ -151,6 +151,16 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
151
151
private final Duration partitionedDmlTimeout ;
152
152
private final boolean grpcGcpExtensionEnabled ;
153
153
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 ;
154
164
private final boolean autoThrottleAdministrativeRequests ;
155
165
private final RetrySettings retryAdministrativeRequestsSettings ;
156
166
private final boolean trackTransactionStarter ;
@@ -788,6 +798,13 @@ protected SpannerOptions(Builder builder) {
788
798
partitionedDmlTimeout = builder .partitionedDmlTimeout ;
789
799
grpcGcpExtensionEnabled = builder .grpcGcpExtensionEnabled ;
790
800
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 ;
791
808
autoThrottleAdministrativeRequests = builder .autoThrottleAdministrativeRequests ;
792
809
retryAdministrativeRequestsSettings = builder .retryAdministrativeRequestsSettings ;
793
810
trackTransactionStarter = builder .trackTransactionStarter ;
@@ -1002,6 +1019,10 @@ public static class Builder
1002
1019
private Duration partitionedDmlTimeout = Duration .ofHours (2L );
1003
1020
private boolean grpcGcpExtensionEnabled = false ;
1004
1021
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 ;
1005
1026
private RetrySettings retryAdministrativeRequestsSettings =
1006
1027
DEFAULT_ADMIN_REQUESTS_LIMIT_EXCEEDED_RETRY_SETTINGS ;
1007
1028
private boolean autoThrottleAdministrativeRequests = false ;
@@ -1025,6 +1046,14 @@ public static class Builder
1025
1046
private boolean isExperimentalHost = false ;
1026
1047
private TransactionOptions defaultTransactionOptions = TransactionOptions .getDefaultInstance ();
1027
1048
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
+
1028
1057
private static String createCustomClientLibToken (String token ) {
1029
1058
return token + " " + ServiceOptions .getGoogApiClientLibName ();
1030
1059
}
@@ -1532,30 +1561,87 @@ public Builder setExperimentalHost(String host) {
1532
1561
return this ;
1533
1562
}
1534
1563
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. */
1540
1565
public Builder enableGrpcGcpExtension () {
1541
1566
return this .enableGrpcGcpExtension (null );
1542
1567
}
1543
1568
1544
1569
/**
1545
1570
* 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.
1549
1572
*/
1550
1573
public Builder enableGrpcGcpExtension (GcpManagedChannelOptions options ) {
1551
1574
this .grpcGcpExtensionEnabled = true ;
1552
1575
this .grpcGcpOptions = options ;
1576
+ this .grpcGcpExtensionExplicitlySet = true ;
1553
1577
return this ;
1554
1578
}
1555
1579
1556
1580
/** Disables gRPC-GCP extension. */
1557
1581
public Builder disableGrpcGcpExtension () {
1558
1582
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 ;
1559
1645
return this ;
1560
1646
}
1561
1647
@@ -1756,6 +1842,15 @@ public SpannerOptions build() {
1756
1842
} else if (isExperimentalHost && credentials == null ) {
1757
1843
credentials = environment .getDefaultExperimentalHostCredentials ();
1758
1844
}
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
+
1759
1854
if (this .numChannels == null ) {
1760
1855
this .numChannels =
1761
1856
this .grpcGcpExtensionEnabled ? GRPC_GCP_ENABLED_DEFAULT_CHANNELS : DEFAULT_CHANNELS ;
@@ -1960,6 +2055,19 @@ public GcpManagedChannelOptions getGrpcGcpOptions() {
1960
2055
return grpcGcpOptions ;
1961
2056
}
1962
2057
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
+
1963
2071
public boolean isAutoThrottleAdministrativeRequests () {
1964
2072
return autoThrottleAdministrativeRequests ;
1965
2073
}
0 commit comments