@@ -172,6 +172,8 @@ public String[] getValidValues() {
172172 private static final RpcPriority DEFAULT_RPC_PRIORITY = null ;
173173 private static final boolean DEFAULT_RETURN_COMMIT_STATS = false ;
174174 private static final boolean DEFAULT_LENIENT = false ;
175+ private static final boolean DEFAULT_TRACK_SESSION_LEAKS = true ;
176+ private static final boolean DEFAULT_TRACK_CONNECTION_LEAKS = true ;
175177
176178 private static final String PLAIN_TEXT_PROTOCOL = "http:" ;
177179 private static final String HOST_PROTOCOL = "https:" ;
@@ -218,6 +220,10 @@ public String[] getValidValues() {
218220 private static final String DIALECT_PROPERTY_NAME = "dialect" ;
219221 /** Name of the 'databaseRole' connection property. */
220222 public static final String DATABASE_ROLE_PROPERTY_NAME = "databaseRole" ;
223+ /** Name of the 'trackStackTraceOfSessionCheckout' connection property. */
224+ public static final String TRACK_SESSION_LEAKS_PROPERTY_NAME = "trackSessionLeaks" ;
225+ /** Name of the 'trackStackTraceOfConnectionCreation' connection property. */
226+ public static final String TRACK_CONNECTION_LEAKS_PROPERTY_NAME = "trackConnectionLeaks" ;
221227 /** All valid connection properties. */
222228 public static final Set <ConnectionProperty > VALID_PROPERTIES =
223229 Collections .unmodifiableSet (
@@ -287,7 +293,25 @@ public String[] getValidValues() {
287293 DIALECT_PROPERTY_NAME , "Sets the dialect to use for this connection." ),
288294 ConnectionProperty .createStringProperty (
289295 DATABASE_ROLE_PROPERTY_NAME ,
290- "Sets the database role to use for this connection. The default is privileges assigned to IAM role" ))));
296+ "Sets the database role to use for this connection. The default is privileges assigned to IAM role" ),
297+ ConnectionProperty .createBooleanProperty (
298+ TRACK_SESSION_LEAKS_PROPERTY_NAME ,
299+ "Capture the call stack of the thread that checked out a session of the session pool. This will "
300+ + "pre-create a LeakedSessionException already when a session is checked out. This can be disabled, "
301+ + "for example if a monitoring system logs the pre-created exception. "
302+ + "If disabled, the LeakedSessionException will only be created when an "
303+ + "actual session leak is detected. The stack trace of the exception will "
304+ + "in that case not contain the call stack of when the session was checked out." ,
305+ DEFAULT_TRACK_SESSION_LEAKS ),
306+ ConnectionProperty .createBooleanProperty (
307+ TRACK_CONNECTION_LEAKS_PROPERTY_NAME ,
308+ "Capture the call stack of the thread that created a connection. This will "
309+ + "pre-create a LeakedConnectionException already when a connection is created. "
310+ + "This can be disabled, for example if a monitoring system logs the pre-created exception. "
311+ + "If disabled, the LeakedConnectionException will only be created when an "
312+ + "actual connection leak is detected. The stack trace of the exception will "
313+ + "in that case not contain the call stack of when the connection was created." ,
314+ DEFAULT_TRACK_CONNECTION_LEAKS ))));
291315
292316 private static final Set <ConnectionProperty > INTERNAL_PROPERTIES =
293317 Collections .unmodifiableSet (
@@ -544,6 +568,8 @@ public static Builder newBuilder() {
544568 private final boolean returnCommitStats ;
545569 private final boolean autoConfigEmulator ;
546570 private final RpcPriority rpcPriority ;
571+ private final boolean trackSessionLeaks ;
572+ private final boolean trackConnectionLeaks ;
547573
548574 private final boolean autocommit ;
549575 private final boolean readOnly ;
@@ -588,6 +614,8 @@ private ConnectionOptions(Builder builder) {
588614 this .usePlainText = this .autoConfigEmulator || parseUsePlainText (this .uri );
589615 this .host = determineHost (matcher , autoConfigEmulator , usePlainText );
590616 this .rpcPriority = parseRPCPriority (this .uri );
617+ this .trackSessionLeaks = parseTrackSessionLeaks (this .uri );
618+ this .trackConnectionLeaks = parseTrackConnectionLeaks (this .uri );
591619
592620 this .instanceId = matcher .group (Builder .INSTANCE_GROUP );
593621 this .databaseName = matcher .group (Builder .DATABASE_GROUP );
@@ -641,11 +669,12 @@ private ConnectionOptions(Builder builder) {
641669 Collections .unmodifiableList (builder .statementExecutionInterceptors );
642670 this .configurator = builder .configurator ;
643671
644- if (this .minSessions != null || this .maxSessions != null ) {
672+ if (this .minSessions != null || this .maxSessions != null || ! this . trackSessionLeaks ) {
645673 SessionPoolOptions .Builder sessionPoolOptionsBuilder =
646674 builder .sessionPoolOptions == null
647675 ? SessionPoolOptions .newBuilder ()
648676 : builder .sessionPoolOptions .toBuilder ();
677+ sessionPoolOptionsBuilder .setTrackStackTraceOfSessionCheckout (this .trackSessionLeaks );
649678 sessionPoolOptionsBuilder .setAutoDetectDialect (true );
650679 if (this .minSessions != null ) {
651680 sessionPoolOptionsBuilder .setMinSessions (this .minSessions );
@@ -838,6 +867,18 @@ static boolean parseLenient(String uri) {
838867 return value != null ? Boolean .parseBoolean (value ) : DEFAULT_LENIENT ;
839868 }
840869
870+ @ VisibleForTesting
871+ static boolean parseTrackSessionLeaks (String uri ) {
872+ String value = parseUriProperty (uri , TRACK_SESSION_LEAKS_PROPERTY_NAME );
873+ return value != null ? Boolean .parseBoolean (value ) : DEFAULT_TRACK_SESSION_LEAKS ;
874+ }
875+
876+ @ VisibleForTesting
877+ static boolean parseTrackConnectionLeaks (String uri ) {
878+ String value = parseUriProperty (uri , TRACK_CONNECTION_LEAKS_PROPERTY_NAME );
879+ return value != null ? Boolean .parseBoolean (value ) : DEFAULT_TRACK_CONNECTION_LEAKS ;
880+ }
881+
841882 @ VisibleForTesting
842883 static RpcPriority parseRPCPriority (String uri ) {
843884 String value = parseUriProperty (uri , RPC_PRIORITY_NAME );
@@ -1078,6 +1119,10 @@ RpcPriority getRPCPriority() {
10781119 return rpcPriority ;
10791120 }
10801121
1122+ boolean isTrackConnectionLeaks () {
1123+ return this .trackConnectionLeaks ;
1124+ }
1125+
10811126 /** Interceptors that should be executed after each statement */
10821127 List <StatementExecutionInterceptor > getStatementExecutionInterceptors () {
10831128 return statementExecutionInterceptors ;
0 commit comments