Thanks to visit codestin.com
Credit goes to github.com

Skip to content

spanner: allowNestedTransaction() of TransactionRunner causes SessionPool$LeakedSessionException #6813

@skuruppu

Description

@skuruppu

When using TransactionRunner.allowNestedTransaction, the outermost (first in order) RW transaction which allows others transaction to be nested do not return a session to the pool back.

Environment details

Env:
Library version: 1.43.0 (com.google.cloud:google-cloud-spanner:1.43.0)
Java version: 1.8.0_211

Code example

SpannerOptions options = SpannerOptions.newBuilder()
                .setProjectId("<project id>")
                .setCredentials(buildCredentials())
                .setSessionPoolOption(SessionPoolOptions.newBuilder()
                        .setMinSessions(4)
                        .setMaxSessions(4)
                        .setFailIfPoolExhausted()
                        .build())
                .build();

        try (Spanner spanner = options.getService()) {
            DatabaseId dbOne = DatabaseId.of(options.getProjectId(), "instanceOne", "db");
            DatabaseId dbTwo = DatabaseId.of(options.getProjectId(), "instanceTwo, "db");
            DatabaseClient dbClientOne = spanner.getDatabaseClient(dbOne);
            DatabaseClient dbClientTwo = spanner.getDatabaseClient(dbTwo);

            for (int i = 0; i < 10; i++) {
                Long result = dbClientOne.readWriteTransaction().allowNestedTransaction().run(transaction -> {
                    try (ResultSet resultSet = transaction.executeQuery(Statement.of("SELECT 1"))) {
                        Long add = dbClientTwo.readWriteTransaction().run(transactionNested -> {
                                    try (ResultSet resultSetTwo = transactionNested.executeQuery(Statement.of("SELECT 2"))) {
                                        resultSetTwo.next();
                                        return resultSetTwo.getLong(0);
                                    }
                                });
                        resultSet.next() ;
                        return (resultSet.getLong(0) + add);
                    }
                });
                System.out.println(i + " " + result);
            } // fails
        }

Other code sample (it fails also when there's a single transaction executed, no real nesting, only the option is on):

for (int i = 0; i < 10; i++) {
                Long result = dbClient.readWriteTransaction().allowNestedTransaction().run(transaction -> {
                    try (ResultSet resultSet = transaction.executeQuery(Statement.of("SELECT 1"))) {
                        resultSet.next();
                        return (resultSet.getLong(0));
                    }
                });
                System.out.println(i + " " + result);
            } // fails

Stack trace

Nov 04, 2019 10:53:24 AM com.google.cloud.spanner.SessionPool closeAsync
WARNING: Leaked session
com.google.cloud.spanner.SessionPool$LeakedSessionException: Session was checked out from the pool at 2019-11-04T08:53:22.802Z
        at com.google.cloud.spanner.SessionPool$PooledSession.markBusy(SessionPool.java:624)
        at com.google.cloud.spanner.SessionPool$PooledSession.access$4400(SessionPool.java:596)
        at com.google.cloud.spanner.SessionPool.getReadWriteSession(SessionPool.java:1319)
        at com.google.cloud.spanner.DatabaseClientImpl.getReadWriteSession(DatabaseClientImpl.java:57)
        at com.google.cloud.spanner.DatabaseClientImpl.readWriteTransaction(DatabaseClientImpl.java:170)
        at LeakTest.testWithAllowNestedTransactions(LeakTest.java:31)

Metadata

Metadata

Assignees

Labels

api: spannerIssues related to the Spanner API.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions