Skip to content

Commit eb3ff4b

Browse files
authored
fix(3.3.x): sessions were not always removed from checkedOutSessions (#1450)
* 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 * test: skip slow tests on stable branch
1 parent 5814690 commit eb3ff4b

File tree

6 files changed

+120
-18
lines changed

6 files changed

+120
-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
@@ -1253,25 +1253,27 @@ public void prepareReadWriteTransaction() {
12531253

12541254
@Override
12551255
public void close() {
1256-
synchronized (lock) {
1257-
leakedException = null;
1258-
checkedOutSessions.remove(this);
1259-
}
1260-
PooledSession delegate = getOrNull();
1261-
if (delegate != null) {
1262-
delegate.close();
1256+
try {
1257+
asyncClose().get();
1258+
} catch (InterruptedException e) {
1259+
throw SpannerExceptionFactory.propagateInterrupt(e);
1260+
} catch (ExecutionException e) {
1261+
throw SpannerExceptionFactory.asSpannerException(e.getCause());
12631262
}
12641263
}
12651264

12661265
@Override
12671266
public ApiFuture<Empty> asyncClose() {
1268-
synchronized (lock) {
1269-
leakedException = null;
1270-
checkedOutSessions.remove(this);
1271-
}
1272-
PooledSession delegate = getOrNull();
1273-
if (delegate != null) {
1274-
return delegate.asyncClose();
1267+
try {
1268+
PooledSession delegate = getOrNull();
1269+
if (delegate != null) {
1270+
return delegate.asyncClose();
1271+
}
1272+
} finally {
1273+
synchronized (lock) {
1274+
leakedException = null;
1275+
checkedOutSessions.remove(this);
1276+
}
12751277
}
12761278
return ApiFutures.immediateFuture(Empty.getDefaultInstance());
12771279
}
@@ -1788,7 +1790,8 @@ private static enum Position {
17881790
private final Set<PooledSession> allSessions = new HashSet<>();
17891791

17901792
@GuardedBy("lock")
1791-
private final Set<PooledSessionFuture> checkedOutSessions = new HashSet<>();
1793+
@VisibleForTesting
1794+
final Set<PooledSessionFuture> checkedOutSessions = new HashSet<>();
17921795

17931796
private final SessionConsumer sessionConsumer = new SessionConsumerImpl();
17941797

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

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import java.util.ArrayList;
5656
import java.util.Arrays;
5757
import java.util.List;
58+
import java.util.Set;
5859
import java.util.concurrent.ExecutionException;
5960
import java.util.concurrent.ExecutorService;
6061
import java.util.concurrent.Executors;
@@ -174,13 +175,18 @@ public void writeAtLeastOnce() {
174175

175176
@Test
176177
public void singleUse() {
177-
DatabaseClient client =
178-
spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
178+
DatabaseClientImpl client =
179+
(DatabaseClientImpl)
180+
spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
181+
Set<PooledSessionFuture> checkedOut = client.pool.checkedOutSessions;
182+
assertThat(checkedOut).isEmpty();
179183
try (ResultSet rs = client.singleUse().executeQuery(SELECT1)) {
180184
assertThat(rs.next()).isTrue();
185+
assertThat(checkedOut).hasSize(1);
181186
assertThat(rs.getLong(0)).isEqualTo(1L);
182187
assertThat(rs.next()).isFalse();
183188
}
189+
assertThat(checkedOut).isEmpty();
184190
}
185191

186192
@Test
@@ -1541,4 +1547,71 @@ public void testTransactionManagerAsync_usesOptions() {
15411547

15421548
verify(session).transactionManagerAsync(option);
15431549
}
1550+
1551+
@Test
1552+
public void singleUseNoAction_ClearsCheckedOutSession() {
1553+
DatabaseClientImpl client =
1554+
(DatabaseClientImpl)
1555+
spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
1556+
Set<PooledSessionFuture> checkedOut = client.pool.checkedOutSessions;
1557+
assertThat(checkedOut).isEmpty();
1558+
1559+
// Getting a single use read-only transaction and not using it should not cause any sessions
1560+
// to be stuck in the map of checked out sessions.
1561+
client.singleUse().close();
1562+
1563+
assertThat(checkedOut).isEmpty();
1564+
}
1565+
1566+
@Test
1567+
public void singleUseReadOnlyTransactionNoAction_ClearsCheckedOutSession() {
1568+
DatabaseClientImpl client =
1569+
(DatabaseClientImpl)
1570+
spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
1571+
Set<PooledSessionFuture> checkedOut = client.pool.checkedOutSessions;
1572+
assertThat(checkedOut).isEmpty();
1573+
1574+
client.singleUseReadOnlyTransaction().close();
1575+
1576+
assertThat(checkedOut).isEmpty();
1577+
}
1578+
1579+
@Test
1580+
public void readWriteTransactionNoAction_ClearsCheckedOutSession() {
1581+
DatabaseClientImpl client =
1582+
(DatabaseClientImpl)
1583+
spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
1584+
Set<PooledSessionFuture> checkedOut = client.pool.checkedOutSessions;
1585+
assertThat(checkedOut).isEmpty();
1586+
1587+
client.readWriteTransaction();
1588+
1589+
assertThat(checkedOut).isEmpty();
1590+
}
1591+
1592+
@Test
1593+
public void readOnlyTransactionNoAction_ClearsCheckedOutSession() {
1594+
DatabaseClientImpl client =
1595+
(DatabaseClientImpl)
1596+
spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
1597+
Set<PooledSessionFuture> checkedOut = client.pool.checkedOutSessions;
1598+
assertThat(checkedOut).isEmpty();
1599+
1600+
client.readOnlyTransaction().close();
1601+
1602+
assertThat(checkedOut).isEmpty();
1603+
}
1604+
1605+
@Test
1606+
public void transactionManagerNoAction_ClearsCheckedOutSession() {
1607+
DatabaseClientImpl client =
1608+
(DatabaseClientImpl)
1609+
spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
1610+
Set<PooledSessionFuture> checkedOut = client.pool.checkedOutSessions;
1611+
assertThat(checkedOut).isEmpty();
1612+
1613+
client.transactionManager().close();
1614+
1615+
assertThat(checkedOut).isEmpty();
1616+
}
15441617
}

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)