@@ -281,3 +281,85 @@ This will cause the following to happen internally in the client library:
2812811 . The ` TransactionRunner ` will automatically commit the transaction if the supplied user code
282282 finished without any errors. The ` Commit ` RPC that is invoked uses a thread from the default gRPC
283283 thread pool.
284+
285+ ### Session Leak
286+ A Spanner object of the Client Library has a limit on the number of maximum sessions. For example the
287+ default value of ` MaxSessions ` in the Java Client Library is 400. You can configure these values at the time of
288+ Client side Database object creation by passing in the ` SessionPoolOptions ` . When all the sessions are checked
289+ out of the session pool, every new transaction has to wait until a session is returned to the pool.
290+ If a session is never returned to the pool (hence causing a session leak), the transactions will have to wait
291+ indefinitely and your application will be blocked.
292+
293+ #### Common Root Causes
294+ The most common reason for a session leak is that a transaction was started by the application but
295+ never committed or rolled back. What you should do is simply call commit on the transaction at the
296+ end of your transaction code. Spanner has two types of transactions, read only and read-write
297+ transactions. When we perform a read in a read-write transaction we still need to commit it.
298+
299+ As shown in the example below, the ` try-with-resources ` block releases the session after it is complete.
300+ If you don't use ` try-with-resources ` block, unless you explicitly call the ` close() ` method on all resources
301+ such as ` ResultSet ` the session is not released back to the pool. If the transaction does not run within
302+ ` try-with-resources ` block and if we don't close the resources explicitly, we will have a session leak.
303+
304+ ``` java
305+ DatabaseClient client =
306+ spanner. getDatabaseClient(DatabaseId . of(" my-project" , " my-instance" , " my-database" ));
307+ try (ResultSet resultSet =
308+ client. singleUse(). executeQuery(Statement . of(" select col1, col2 from my_table" ))) {
309+ while (resultSet. next()) {
310+ // use the results.
311+ }
312+ }
313+ ```
314+
315+ #### Debugging and Resolving Session Leaks
316+
317+ ##### Logging
318+ Enabled by default, the logging option shares warn logs when you have exhausted >95% of your session pool.
319+ This could mean two things, either you need to increase the max sessions in your session pool (as the number
320+ of queries run using the client side database object is greater than your session pool can serve) or you may
321+ have a session leak.
322+
323+ To help debug which transactions may be causing this session leak, the logs will also contain stack traces of
324+ transactions which have been running longer than expected. The logs are pushed to a destination based on
325+ how the log exporter is configured for the host application.
326+
327+ ``` java
328+ final SessionPoolOptions sessionPoolOptions =
329+ SessionPoolOptions . newBuilder(). setWarnIfInactiveTransactions(). build()
330+
331+ final Spanner spanner =
332+ SpannerOptions . newBuilder()
333+ .setSessionPoolOption(sessionPoolOptions)
334+ .build()
335+ .getService();
336+ final DatabaseClient client = spanner. getDatabaseClient(databaseId);
337+
338+ // Example Log message to warn presence of long running transactions
339+ // Detected long-running session <session-info>. To automatically remove long-running sessions, set SessionOption ActionOnInactiveTransaction
340+ // to WARN_AND_CLOSE by invoking setWarnAndCloseIfInactiveTransactions() method. <Stack Trace and information on session>
341+
342+ ```
343+ ##### Automatically clean inactive transactions
344+ When the option to automatically clean inactive transactions is enabled, the client library will automatically spot
345+ problematic transactions that are running for extremely long periods of time (thus causing session leaks) and close them.
346+ The session will be removed from the pool and be replaced by a new session. To dig deeper into which transactions are being
347+ closed, you can check the logs to see the stack trace of the transactions which might be causing these leaks and further
348+ debug them.
349+
350+ ``` java
351+ final SessionPoolOptions sessionPoolOptions =
352+ SessionPoolOptions . newBuilder(). setWarnAndCloseIfInactiveTransactions(). build()
353+
354+ final Spanner spanner =
355+ SpannerOptions . newBuilder()
356+ .setSessionPoolOption(sessionPoolOptions)
357+ .build()
358+ .getService();
359+ final DatabaseClient client = spanner. getDatabaseClient(databaseId);
360+
361+ // Example Log message for when transaction is recycled
362+ // Removing long-running session <Stack Trace and information on session>
363+ ```
364+
365+
0 commit comments