Skip to content

Commit faa7e5d

Browse files
arpan14olavloitegcf-owl-bot[bot]
authored
feat: enable session leaks prevention by cleaning up long-running tra… (#2655)
* feat: enable session leaks prevention by cleaning up long-running transactions. * Update session-and-channel-pool-configuration.md Co-authored-by: Knut Olav Løite <koloite@gmail.com> * Update session-and-channel-pool-configuration.md Co-authored-by: Knut Olav Løite <koloite@gmail.com> * Update session-and-channel-pool-configuration.md Co-authored-by: Knut Olav Løite <koloite@gmail.com> * Update session-and-channel-pool-configuration.md Co-authored-by: Knut Olav Løite <koloite@gmail.com> * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Knut Olav Løite <koloite@gmail.com> Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 7f6b158 commit faa7e5d

File tree

3 files changed

+86
-4
lines changed

3 files changed

+86
-4
lines changed

google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,8 @@ static InactiveTransactionRemovalOptions.Builder newBuilder() {
357357
}
358358

359359
static class Builder {
360-
private ActionOnInactiveTransaction actionOnInactiveTransaction;
360+
private ActionOnInactiveTransaction actionOnInactiveTransaction =
361+
ActionOnInactiveTransaction.WARN;
361362
private Duration executionFrequency = Duration.ofMinutes(2);
362363
private double usedSessionsRatioThreshold = 0.95;
363364
private Duration idleTimeThreshold = Duration.ofMinutes(60L);
@@ -598,7 +599,7 @@ public Builder setBlockIfPoolExhausted() {
598599
*
599600
* @return this builder for chaining
600601
*/
601-
Builder setWarnIfInactiveTransactions() {
602+
public Builder setWarnIfInactiveTransactions() {
602603
this.inactiveTransactionRemovalOptions =
603604
InactiveTransactionRemovalOptions.newBuilder()
604605
.setActionOnInactiveTransaction(ActionOnInactiveTransaction.WARN)
@@ -617,7 +618,7 @@ Builder setWarnIfInactiveTransactions() {
617618
*
618619
* @return this builder for chaining
619620
*/
620-
Builder setWarnAndCloseIfInactiveTransactions() {
621+
public Builder setWarnAndCloseIfInactiveTransactions() {
621622
this.inactiveTransactionRemovalOptions =
622623
InactiveTransactionRemovalOptions.newBuilder()
623624
.setActionOnInactiveTransaction(ActionOnInactiveTransaction.WARN_AND_CLOSE)

google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolOptionsTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public void verifyDefaultInactiveTransactionRemovalOptions() {
129129
InactiveTransactionRemovalOptions inactiveTransactionRemovalOptions =
130130
sessionPoolOptions.getInactiveTransactionRemovalOptions();
131131

132-
assertFalse(sessionPoolOptions.warnInactiveTransactions());
132+
assertTrue(sessionPoolOptions.warnInactiveTransactions());
133133
assertFalse(sessionPoolOptions.warnAndCloseInactiveTransactions());
134134
assertFalse(sessionPoolOptions.closeInactiveTransactions());
135135
assertEquals(0.95, inactiveTransactionRemovalOptions.getUsedSessionsRatioThreshold(), 0.0);

session-and-channel-pool-configuration.md

+81
Original file line numberDiff line numberDiff line change
@@ -281,3 +281,84 @@ This will cause the following to happen internally in the client library:
281281
1. 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 `DatabaseClient` 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+
creating a `Spanner` instance by setting custom `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 session leaks in the Java client library are:
295+
1. Not closing a `ResultSet` that is returned by `executeQuery`. Always put `ResultSet` objects in a try-with-resources block, or take other measures to ensure that the `ResultSet` is always closed.
296+
2. Not closing a `ReadOnlyTransaction` when you no longer need it. Always put `ReadOnlyTransaction` objects in a try-with-resources block, or take other measures to ensure that the `ReadOnlyTransaction` is always closed.
297+
3. Not closing a `TransactionManager` when you no longer need it. Always put `TransactionManager` objects in a try-with-resources block, or take other measures to ensure that the `TransactionManager` is always closed.
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.
302+
303+
```java
304+
DatabaseClient client =
305+
spanner.getDatabaseClient(DatabaseId.of("my-project", "my-instance", "my-database"));
306+
try (ResultSet resultSet =
307+
client.singleUse().executeQuery(Statement.of("select col1, col2 from my_table"))) {
308+
while (resultSet.next()) {
309+
// use the results.
310+
}
311+
}
312+
```
313+
314+
#### Debugging and Resolving Session Leaks
315+
316+
##### Logging
317+
Enabled by default, the logging option shares warn logs when you have exhausted >95% of your session pool.
318+
This could mean two things, either you need to increase the max sessions in your session pool (as the number
319+
of queries run using the client side database object is greater than your session pool can serve) or you may
320+
have a session leak.
321+
322+
To help debug which transactions may be causing this session leak, the logs will also contain stack traces of
323+
transactions which have been running longer than expected. The logs are pushed to a destination based on
324+
how the log exporter is configured for the host application.
325+
326+
``` java
327+
final SessionPoolOptions sessionPoolOptions =
328+
SessionPoolOptions.newBuilder().setWarnIfInactiveTransactions().build()
329+
330+
final Spanner spanner =
331+
SpannerOptions.newBuilder()
332+
.setSessionPoolOption(sessionPoolOptions)
333+
.build()
334+
.getService();
335+
final DatabaseClient client = spanner.getDatabaseClient(databaseId);
336+
337+
// Example Log message to warn presence of long running transactions
338+
// Detected long-running session <session-info>. To automatically remove long-running sessions, set SessionOption ActionOnInactiveTransaction
339+
// to WARN_AND_CLOSE by invoking setWarnAndCloseIfInactiveTransactions() method. <Stack Trace and information on session>
340+
341+
```
342+
##### Automatically clean inactive transactions
343+
When the option to automatically clean inactive transactions is enabled, the client library will automatically spot
344+
problematic transactions that are running for extremely long periods of time (thus causing session leaks) and close them.
345+
The session will be removed from the pool and be replaced by a new session. To dig deeper into which transactions are being
346+
closed, you can check the logs to see the stack trace of the transactions which might be causing these leaks and further
347+
debug them.
348+
349+
``` java
350+
final SessionPoolOptions sessionPoolOptions =
351+
SessionPoolOptions.newBuilder().setWarnAndCloseIfInactiveTransactions().build()
352+
353+
final Spanner spanner =
354+
SpannerOptions.newBuilder()
355+
.setSessionPoolOption(sessionPoolOptions)
356+
.build()
357+
.getService();
358+
final DatabaseClient client = spanner.getDatabaseClient(databaseId);
359+
360+
// Example Log message for when transaction is recycled
361+
// Removing long-running session <Stack Trace and information on session>
362+
```
363+
364+

0 commit comments

Comments
 (0)