Skip to content

Commit 165b31b

Browse files
authored
fix(3.1.x): sessions were not always removed from checkedOutSessions (#1451)
* fix: sessions were not always removed from checkedOutSessions Sessions were added to a set of checked out sessions when one was checked out from the session pool. When the session was released back to the pool, the session would normally be removed from the set of checked out sessions. The latter would not always happen if the application that checked out the session did not use the session for any reads or writes. chore: fix maven version to 3.8.1 chore: fix maven version for windows * fix: remove accidental java version change * test: skip slow tests on stable branch
1 parent 2d05064 commit 165b31b

File tree

6 files changed

+121
-18
lines changed

6 files changed

+121
-18
lines changed

.github/workflows/ci.yaml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ jobs:
1212
java: [7, 8, 11]
1313
steps:
1414
- uses: actions/checkout@v2
15+
- uses: stCarolas/setup-maven@v4
16+
with:
17+
maven-version: 3.8.1
1518
- uses: actions/setup-java@v1
1619
with:
1720
java-version: ${{matrix.java}}
@@ -27,6 +30,9 @@ jobs:
2730
runs-on: windows-latest
2831
steps:
2932
- uses: actions/checkout@v2
33+
- uses: stCarolas/setup-maven@v4
34+
with:
35+
maven-version: 3.8.1
3036
- uses: actions/setup-java@v1
3137
with:
3238
java-version: 8
@@ -41,6 +47,9 @@ jobs:
4147
java: [8, 11]
4248
steps:
4349
- uses: actions/checkout@v2
50+
- uses: stCarolas/setup-maven@v4
51+
with:
52+
maven-version: 3.8.1
4453
- uses: actions/setup-java@v1
4554
with:
4655
java-version: ${{matrix.java}}
@@ -50,6 +59,9 @@ jobs:
5059
runs-on: ubuntu-latest
5160
steps:
5261
- uses: actions/checkout@v2
62+
- uses: stCarolas/setup-maven@v4
63+
with:
64+
maven-version: 3.8.1
5365
- uses: actions/setup-java@v1
5466
with:
5567
java-version: 8
@@ -59,6 +71,9 @@ jobs:
5971
runs-on: ubuntu-latest
6072
steps:
6173
- uses: actions/checkout@v2
74+
- uses: stCarolas/setup-maven@v4
75+
with:
76+
maven-version: 3.8.1
6277
- uses: actions/setup-java@v1
6378
with:
6479
java-version: 8
@@ -70,10 +85,13 @@ jobs:
7085
runs-on: ubuntu-latest
7186
steps:
7287
- uses: actions/checkout@v2
88+
- uses: stCarolas/setup-maven@v4
89+
with:
90+
maven-version: 3.8.1
7391
- uses: actions/setup-java@v1
7492
with:
7593
java-version: 8
7694
- run: java -version
7795
- run: .kokoro/build.sh
7896
env:
79-
JOB_TYPE: clirr
97+
JOB_TYPE: clirr

.github/workflows/integration-tests-against-emulator.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ jobs:
1717

1818
steps:
1919
- uses: actions/checkout@v2
20+
- uses: stCarolas/setup-maven@v4
21+
with:
22+
maven-version: 3.8.1
2023
- uses: actions/setup-java@v1
2124
with:
2225
java-version: 8

.github/workflows/samples.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ jobs:
66
runs-on: ubuntu-latest
77
steps:
88
- uses: actions/checkout@v2
9+
- uses: stCarolas/setup-maven@v4
10+
with:
11+
maven-version: 3.8.1
912
- uses: actions/setup-java@v1
1013
with:
1114
java-version: 8

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

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,25 +1243,27 @@ public void prepareReadWriteTransaction() {
12431243

12441244
@Override
12451245
public void close() {
1246-
synchronized (lock) {
1247-
leakedException = null;
1248-
checkedOutSessions.remove(this);
1249-
}
1250-
PooledSession delegate = getOrNull();
1251-
if (delegate != null) {
1252-
delegate.close();
1246+
try {
1247+
asyncClose().get();
1248+
} catch (InterruptedException e) {
1249+
throw SpannerExceptionFactory.propagateInterrupt(e);
1250+
} catch (ExecutionException e) {
1251+
throw SpannerExceptionFactory.asSpannerException(e.getCause());
12531252
}
12541253
}
12551254

12561255
@Override
12571256
public ApiFuture<Empty> asyncClose() {
1258-
synchronized (lock) {
1259-
leakedException = null;
1260-
checkedOutSessions.remove(this);
1261-
}
1262-
PooledSession delegate = getOrNull();
1263-
if (delegate != null) {
1264-
return delegate.asyncClose();
1257+
try {
1258+
PooledSession delegate = getOrNull();
1259+
if (delegate != null) {
1260+
return delegate.asyncClose();
1261+
}
1262+
} finally {
1263+
synchronized (lock) {
1264+
leakedException = null;
1265+
checkedOutSessions.remove(this);
1266+
}
12651267
}
12661268
return ApiFutures.immediateFuture(Empty.getDefaultInstance());
12671269
}
@@ -1769,7 +1771,8 @@ private static enum Position {
17691771
private final Set<PooledSession> allSessions = new HashSet<>();
17701772

17711773
@GuardedBy("lock")
1772-
private final Set<PooledSessionFuture> checkedOutSessions = new HashSet<>();
1774+
@VisibleForTesting
1775+
final Set<PooledSessionFuture> checkedOutSessions = new HashSet<>();
17731776

17741777
private final SessionConsumer sessionConsumer = new SessionConsumerImpl();
17751778

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

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.google.cloud.spanner.MockSpannerServiceImpl.SimulatedExecutionTime;
3333
import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult;
3434
import com.google.cloud.spanner.ReadContext.QueryAnalyzeMode;
35+
import com.google.cloud.spanner.SessionPool.PooledSessionFuture;
3536
import com.google.cloud.spanner.SpannerOptions.SpannerCallContextTimeoutConfigurator;
3637
import com.google.cloud.spanner.TransactionRunner.TransactionCallable;
3738
import com.google.common.base.Stopwatch;
@@ -50,6 +51,7 @@
5051
import java.util.ArrayList;
5152
import java.util.Arrays;
5253
import java.util.List;
54+
import java.util.Set;
5355
import java.util.concurrent.ExecutionException;
5456
import java.util.concurrent.ExecutorService;
5557
import java.util.concurrent.Executors;
@@ -168,13 +170,18 @@ public void writeAtLeastOnce() {
168170

169171
@Test
170172
public void singleUse() {
171-
DatabaseClient client =
172-
spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
173+
DatabaseClientImpl client =
174+
(DatabaseClientImpl)
175+
spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
176+
Set<PooledSessionFuture> checkedOut = client.pool.checkedOutSessions;
177+
assertThat(checkedOut).isEmpty();
173178
try (ResultSet rs = client.singleUse().executeQuery(SELECT1)) {
174179
assertThat(rs.next()).isTrue();
180+
assertThat(checkedOut).hasSize(1);
175181
assertThat(rs.getLong(0)).isEqualTo(1L);
176182
assertThat(rs.next()).isFalse();
177183
}
184+
assertThat(checkedOut).isEmpty();
178185
}
179186

180187
@Test
@@ -1483,4 +1490,71 @@ public void testBatchCreateSessionsFailure_shouldNotPropagateToCloseMethod() {
14831490
mockSpanner.setBatchCreateSessionsExecutionTime(SimulatedExecutionTime.none());
14841491
}
14851492
}
1493+
1494+
@Test
1495+
public void singleUseNoAction_ClearsCheckedOutSession() {
1496+
DatabaseClientImpl client =
1497+
(DatabaseClientImpl)
1498+
spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
1499+
Set<PooledSessionFuture> checkedOut = client.pool.checkedOutSessions;
1500+
assertThat(checkedOut).isEmpty();
1501+
1502+
// Getting a single use read-only transaction and not using it should not cause any sessions
1503+
// to be stuck in the map of checked out sessions.
1504+
client.singleUse().close();
1505+
1506+
assertThat(checkedOut).isEmpty();
1507+
}
1508+
1509+
@Test
1510+
public void singleUseReadOnlyTransactionNoAction_ClearsCheckedOutSession() {
1511+
DatabaseClientImpl client =
1512+
(DatabaseClientImpl)
1513+
spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
1514+
Set<PooledSessionFuture> checkedOut = client.pool.checkedOutSessions;
1515+
assertThat(checkedOut).isEmpty();
1516+
1517+
client.singleUseReadOnlyTransaction().close();
1518+
1519+
assertThat(checkedOut).isEmpty();
1520+
}
1521+
1522+
@Test
1523+
public void readWriteTransactionNoAction_ClearsCheckedOutSession() {
1524+
DatabaseClientImpl client =
1525+
(DatabaseClientImpl)
1526+
spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
1527+
Set<PooledSessionFuture> checkedOut = client.pool.checkedOutSessions;
1528+
assertThat(checkedOut).isEmpty();
1529+
1530+
client.readWriteTransaction();
1531+
1532+
assertThat(checkedOut).isEmpty();
1533+
}
1534+
1535+
@Test
1536+
public void readOnlyTransactionNoAction_ClearsCheckedOutSession() {
1537+
DatabaseClientImpl client =
1538+
(DatabaseClientImpl)
1539+
spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
1540+
Set<PooledSessionFuture> checkedOut = client.pool.checkedOutSessions;
1541+
assertThat(checkedOut).isEmpty();
1542+
1543+
client.readOnlyTransaction().close();
1544+
1545+
assertThat(checkedOut).isEmpty();
1546+
}
1547+
1548+
@Test
1549+
public void transactionManagerNoAction_ClearsCheckedOutSession() {
1550+
DatabaseClientImpl client =
1551+
(DatabaseClientImpl)
1552+
spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
1553+
Set<PooledSessionFuture> checkedOut = client.pool.checkedOutSessions;
1554+
assertThat(checkedOut).isEmpty();
1555+
1556+
client.transactionManager().close();
1557+
1558+
assertThat(checkedOut).isEmpty();
1559+
}
14861560
}

google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBackupTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import org.junit.Before;
6868
import org.junit.BeforeClass;
6969
import org.junit.ClassRule;
70+
import org.junit.Ignore;
7071
import org.junit.Test;
7172
import org.junit.experimental.categories.Category;
7273
import org.junit.runner.RunWith;
@@ -217,6 +218,7 @@ private Timestamp yesterday() {
217218
}
218219

219220
@Test
221+
@Ignore("Do not run slow test in stable branch")
220222
public void testBackups() throws InterruptedException, ExecutionException {
221223
// Create two test databases in parallel.
222224
String db1Id = testHelper.getUniqueDatabaseId() + "_db1";

0 commit comments

Comments
 (0)