@@ -141,7 +141,7 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
141141
142142 // Dynamic Channel Pool (DCP) default values and bounds
143143 /** Default max concurrent RPCs per channel before triggering scale up. */
144- public static final int DEFAULT_DYNAMIC_POOL_MAX_RPC = 25 ;
144+ public static final int DEFAULT_DYNAMIC_POOL_MAX_RPC = 90 ;
145145
146146 /** Default min concurrent RPCs per channel for scale down check. */
147147 public static final int DEFAULT_DYNAMIC_POOL_MIN_RPC = 15 ;
@@ -150,13 +150,13 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
150150 public static final Duration DEFAULT_DYNAMIC_POOL_SCALE_DOWN_INTERVAL = Duration .ofMinutes (3 );
151151
152152 /** Default initial number of channels for dynamic pool. */
153- public static final int DEFAULT_DYNAMIC_POOL_INITIAL_SIZE = 4 ;
153+ public static final int DEFAULT_DYNAMIC_POOL_INITIAL_SIZE = 1 ;
154154
155155 /** Default max number of channels for dynamic pool. */
156- public static final int DEFAULT_DYNAMIC_POOL_MAX_CHANNELS = 10 ;
156+ public static final int DEFAULT_DYNAMIC_POOL_MAX_CHANNELS = 256 ;
157157
158158 /** Default min number of channels for dynamic pool. */
159- public static final int DEFAULT_DYNAMIC_POOL_MIN_CHANNELS = 2 ;
159+ public static final int DEFAULT_DYNAMIC_POOL_MIN_CHANNELS = 1 ;
160160
161161 /**
162162 * Default affinity key lifetime for dynamic channel pool. This is how long to keep an affinity
@@ -204,6 +204,49 @@ public static GcpChannelPoolOptions createDefaultDynamicChannelPoolOptions() {
204204 .build ();
205205 }
206206
207+ /**
208+ * Merges user-provided {@link GcpChannelPoolOptions} with Spanner-specific defaults. Any value
209+ * that the user has not explicitly set (i.e. left at the builder's default of 0 or null) will be
210+ * filled in from {@link #createDefaultDynamicChannelPoolOptions()}.
211+ */
212+ static GcpChannelPoolOptions mergeWithDefaultChannelPoolOptions (
213+ GcpChannelPoolOptions userOptions ) {
214+ GcpChannelPoolOptions defaults = createDefaultDynamicChannelPoolOptions ();
215+ GcpChannelPoolOptions .Builder merged = GcpChannelPoolOptions .newBuilder (userOptions );
216+ if (userOptions .getMaxSize () <= 0 ) {
217+ merged .setMaxSize (defaults .getMaxSize ());
218+ }
219+ if (userOptions .getMinSize () <= 0 ) {
220+ merged .setMinSize (defaults .getMinSize ());
221+ }
222+ if (userOptions .getInitSize () <= 0 ) {
223+ merged .setInitSize (defaults .getInitSize ());
224+ }
225+ if (userOptions .getMinRpcPerChannel () <= 0
226+ || userOptions .getMaxRpcPerChannel () <= 0
227+ || userOptions .getScaleDownInterval () == null
228+ || userOptions .getScaleDownInterval ().isZero ()) {
229+ merged .setDynamicScaling (
230+ userOptions .getMinRpcPerChannel () > 0
231+ ? userOptions .getMinRpcPerChannel ()
232+ : defaults .getMinRpcPerChannel (),
233+ userOptions .getMaxRpcPerChannel () > 0
234+ ? userOptions .getMaxRpcPerChannel ()
235+ : defaults .getMaxRpcPerChannel (),
236+ userOptions .getScaleDownInterval () != null && !userOptions .getScaleDownInterval ().isZero ()
237+ ? userOptions .getScaleDownInterval ()
238+ : defaults .getScaleDownInterval ());
239+ }
240+ if (userOptions .getAffinityKeyLifetime () == null
241+ || userOptions .getAffinityKeyLifetime ().isZero ()) {
242+ merged .setAffinityKeyLifetime (defaults .getAffinityKeyLifetime ());
243+ }
244+ if (userOptions .getCleanupInterval () == null || userOptions .getCleanupInterval ().isZero ()) {
245+ merged .setCleanupInterval (defaults .getCleanupInterval ());
246+ }
247+ return merged .build ();
248+ }
249+
207250 private final TransportChannelProvider channelProvider ;
208251 private final ChannelEndpointCacheFactory channelEndpointCacheFactory ;
209252
@@ -881,21 +924,29 @@ protected SpannerOptions(Builder builder) {
881924
882925 // Dynamic channel pooling is disabled by default.
883926 // It is only enabled when:
884- // 1. enableDynamicChannelPool() was explicitly called, AND
927+ // 1. enableDynamicChannelPool() was explicitly called (or experimentalHost is set and DCP was
928+ // not explicitly disabled), AND
885929 // 2. grpc-gcp extension is enabled, AND
886930 // 3. numChannels was not explicitly set
887- if (builder .dynamicChannelPoolEnabled != null && builder .dynamicChannelPoolEnabled ) {
888- // DCP was explicitly enabled, but respect numChannels if set
931+ boolean dcpRequested =
932+ builder .dynamicChannelPoolEnabled != null
933+ ? builder .dynamicChannelPoolEnabled
934+ : builder .experimentalHost != null ;
935+ if (dcpRequested ) {
936+ // DCP was enabled (explicitly or via experimentalHost), but respect numChannels if set
889937 dynamicChannelPoolEnabled = grpcGcpExtensionEnabled && !builder .numChannelsExplicitlySet ;
890938 } else {
891939 // DCP is disabled by default, or was explicitly disabled
892940 dynamicChannelPoolEnabled = false ;
893941 }
894942
895- // Use user-provided GcpChannelPoolOptions or create Spanner-specific defaults
943+ // Use user-provided GcpChannelPoolOptions merged with Spanner-specific defaults,
944+ // or create Spanner-specific defaults if no custom options are provided.
945+ // This ensures that dynamic scaling parameters (minRpcPerChannel, maxRpcPerChannel,
946+ // scaleDownInterval) retain their defaults even when the user only customizes pool sizing.
896947 gcpChannelPoolOptions =
897948 builder .gcpChannelPoolOptions != null
898- ? builder .gcpChannelPoolOptions
949+ ? mergeWithDefaultChannelPoolOptions ( builder .gcpChannelPoolOptions )
899950 : createDefaultDynamicChannelPoolOptions ();
900951
901952 autoThrottleAdministrativeRequests = builder .autoThrottleAdministrativeRequests ;
@@ -930,7 +981,14 @@ protected SpannerOptions(Builder builder) {
930981 } else {
931982 enableBuiltInMetrics = builder .enableBuiltInMetrics ;
932983 }
933- enableLocationApi = builder .enableLocationApi ;
984+ // Enable location API when experimental host is set, unless explicitly disabled
985+ // via GOOGLE_SPANNER_EXPERIMENTAL_LOCATION_API=false.
986+ if (builder .experimentalHost != null ) {
987+ String locationApiEnvValue = System .getenv (EXPERIMENTAL_LOCATION_API_ENV_VAR );
988+ enableLocationApi = locationApiEnvValue == null || Boolean .parseBoolean (locationApiEnvValue );
989+ } else {
990+ enableLocationApi = builder .enableLocationApi ;
991+ }
934992 enableEndToEndTracing = builder .enableEndToEndTracing ;
935993 monitoringHost = builder .monitoringHost ;
936994 defaultTransactionOptions = builder .defaultTransactionOptions ;
0 commit comments