@@ -150,6 +150,8 @@ public String[] getValidValues() {
150150 static final boolean DEFAULT_RETRY_ABORTS_INTERNALLY = true ;
151151 private static final String DEFAULT_CREDENTIALS = null ;
152152 private static final String DEFAULT_OAUTH_TOKEN = null ;
153+ private static final String DEFAULT_MIN_SESSIONS = null ;
154+ private static final String DEFAULT_MAX_SESSIONS = null ;
153155 private static final String DEFAULT_NUM_CHANNELS = null ;
154156 private static final String DEFAULT_USER_AGENT = null ;
155157 private static final String DEFAULT_OPTIMIZER_VERSION = "" ;
@@ -172,6 +174,10 @@ public String[] getValidValues() {
172174 * OAuth token to use for authentication. Cannot be used in combination with a credentials file.
173175 */
174176 public static final String OAUTH_TOKEN_PROPERTY_NAME = "oauthToken" ;
177+ /** Name of the 'minSessions' connection property. */
178+ public static final String MIN_SESSIONS_PROPERTY_NAME = "minSessions" ;
179+ /** Name of the 'numChannels' connection property. */
180+ public static final String MAX_SESSIONS_PROPERTY_NAME = "maxSessions" ;
175181 /** Name of the 'numChannels' connection property. */
176182 public static final String NUM_CHANNELS_PROPERTY_NAME = "numChannels" ;
177183 /** Custom user agent string is only for other Google libraries. */
@@ -204,6 +210,12 @@ public String[] getValidValues() {
204210 ConnectionProperty .createStringProperty (
205211 OAUTH_TOKEN_PROPERTY_NAME ,
206212 "A valid pre-existing OAuth token to use for authentication for this connection. Setting this property will take precedence over any value set for a credentials file." ),
213+ ConnectionProperty .createStringProperty (
214+ MIN_SESSIONS_PROPERTY_NAME ,
215+ "The minimum number of sessions in the backing session pool. The default is 100." ),
216+ ConnectionProperty .createStringProperty (
217+ MAX_SESSIONS_PROPERTY_NAME ,
218+ "The maximum number of sessions in the backing session pool. The default is 400." ),
207219 ConnectionProperty .createStringProperty (
208220 NUM_CHANNELS_PROPERTY_NAME ,
209221 "The number of gRPC channels to use to communicate with Cloud Spanner. The default is 4." ),
@@ -327,6 +339,9 @@ private boolean isValidUri(String uri) {
327339 * true.
328340 * <li>readonly (boolean): Sets the initial readonly mode for the connection. Default is
329341 * false.
342+ * <li>minSessions (int): Sets the minimum number of sessions in the backing session pool.
343+ * <li>maxSessions (int): Sets the maximum number of sessions in the backing session pool.
344+ * <li>numChannels (int): Sets the number of gRPC channels to use for the connection.
330345 * <li>retryAbortsInternally (boolean): Sets the initial retryAbortsInternally mode for the
331346 * connection. Default is true.
332347 * <li>optimizerVersion (string): Sets the query optimizer version to use for the connection.
@@ -437,6 +452,8 @@ public static Builder newBuilder() {
437452 private final Credentials credentials ;
438453 private final SessionPoolOptions sessionPoolOptions ;
439454 private final Integer numChannels ;
455+ private final Integer minSessions ;
456+ private final Integer maxSessions ;
440457 private final String userAgent ;
441458 private final QueryOptions queryOptions ;
442459
@@ -453,7 +470,6 @@ private ConnectionOptions(Builder builder) {
453470 this .warnings = checkValidProperties (builder .uri );
454471
455472 this .uri = builder .uri ;
456- this .sessionPoolOptions = builder .sessionPoolOptions ;
457473 this .credentialsUrl =
458474 builder .credentialsUrl != null ? builder .credentialsUrl : parseCredentials (builder .uri );
459475 this .oauthToken =
@@ -492,19 +508,12 @@ private ConnectionOptions(Builder builder) {
492508 } else {
493509 this .credentials = getCredentialsService ().createCredentials (this .credentialsUrl );
494510 }
495- String numChannelsValue = parseNumChannels (builder .uri );
496- if (numChannelsValue != null ) {
497- try {
498- this .numChannels = Integer .valueOf (numChannelsValue );
499- } catch (NumberFormatException e ) {
500- throw SpannerExceptionFactory .newSpannerException (
501- ErrorCode .INVALID_ARGUMENT ,
502- "Invalid numChannels value specified: " + numChannelsValue ,
503- e );
504- }
505- } else {
506- this .numChannels = null ;
507- }
511+ this .minSessions =
512+ parseIntegerProperty (MIN_SESSIONS_PROPERTY_NAME , parseMinSessions (builder .uri ));
513+ this .maxSessions =
514+ parseIntegerProperty (MAX_SESSIONS_PROPERTY_NAME , parseMaxSessions (builder .uri ));
515+ this .numChannels =
516+ parseIntegerProperty (NUM_CHANNELS_PROPERTY_NAME , parseNumChannels (builder .uri ));
508517
509518 String projectId = matcher .group (Builder .PROJECT_GROUP );
510519 if (Builder .DEFAULT_PROJECT_ID_PLACEHOLDER .equalsIgnoreCase (projectId )) {
@@ -518,6 +527,36 @@ private ConnectionOptions(Builder builder) {
518527 this .statementExecutionInterceptors =
519528 Collections .unmodifiableList (builder .statementExecutionInterceptors );
520529 this .configurator = builder .configurator ;
530+
531+ if (this .minSessions != null || this .maxSessions != null ) {
532+ SessionPoolOptions .Builder sessionPoolOptionsBuilder =
533+ builder .sessionPoolOptions == null
534+ ? SessionPoolOptions .newBuilder ()
535+ : builder .sessionPoolOptions .toBuilder ();
536+ if (this .minSessions != null ) {
537+ sessionPoolOptionsBuilder .setMinSessions (this .minSessions );
538+ }
539+ if (this .maxSessions != null ) {
540+ sessionPoolOptionsBuilder .setMaxSessions (this .maxSessions );
541+ }
542+ this .sessionPoolOptions = sessionPoolOptionsBuilder .build ();
543+ } else {
544+ this .sessionPoolOptions = builder .sessionPoolOptions ;
545+ }
546+ }
547+
548+ private static Integer parseIntegerProperty (String propertyName , String value ) {
549+ if (value != null ) {
550+ try {
551+ return Integer .valueOf (value );
552+ } catch (NumberFormatException e ) {
553+ throw SpannerExceptionFactory .newSpannerException (
554+ ErrorCode .INVALID_ARGUMENT ,
555+ String .format ("Invalid %s value specified: %s" , propertyName , value ),
556+ e );
557+ }
558+ }
559+ return null ;
521560 }
522561
523562 SpannerOptionsConfigurator getConfigurator () {
@@ -565,6 +604,18 @@ static String parseOAuthToken(String uri) {
565604 return value != null ? value : DEFAULT_OAUTH_TOKEN ;
566605 }
567606
607+ @ VisibleForTesting
608+ static String parseMinSessions (String uri ) {
609+ String value = parseUriProperty (uri , MIN_SESSIONS_PROPERTY_NAME );
610+ return value != null ? value : DEFAULT_MIN_SESSIONS ;
611+ }
612+
613+ @ VisibleForTesting
614+ static String parseMaxSessions (String uri ) {
615+ String value = parseUriProperty (uri , MAX_SESSIONS_PROPERTY_NAME );
616+ return value != null ? value : DEFAULT_MAX_SESSIONS ;
617+ }
618+
568619 @ VisibleForTesting
569620 static String parseNumChannels (String uri ) {
570621 String value = parseUriProperty (uri , NUM_CHANNELS_PROPERTY_NAME );
@@ -671,6 +722,24 @@ public SessionPoolOptions getSessionPoolOptions() {
671722 return sessionPoolOptions ;
672723 }
673724
725+ /**
726+ * The minimum number of sessions in the backing session pool of this connection. The session pool
727+ * is shared between all connections in the same JVM that connect to the same Cloud Spanner
728+ * database using the same connection settings.
729+ */
730+ public Integer getMinSessions () {
731+ return minSessions ;
732+ }
733+
734+ /**
735+ * The maximum number of sessions in the backing session pool of this connection. The session pool
736+ * is shared between all connections in the same JVM that connect to the same Cloud Spanner
737+ * database using the same connection settings.
738+ */
739+ public Integer getMaxSessions () {
740+ return maxSessions ;
741+ }
742+
674743 /** The number of channels to use for the connection. */
675744 public Integer getNumChannels () {
676745 return numChannels ;
0 commit comments