Skip to content

Commit a5862a5

Browse files
authored
feat: allow setting min/max sessions (#335)
* feat: allow setting min/max sessions * chore: run code formatter
1 parent eff34a0 commit a5862a5

File tree

3 files changed

+130
-16
lines changed

3 files changed

+130
-16
lines changed

src/main/java/com/google/cloud/spanner/jdbc/JdbcDriver.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@
8888
* connection. Default is true. @see {@link
8989
* com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection#setRetryAbortsInternally(boolean)}
9090
* for more information.
91+
* <li>minSessions (int): Sets the minimum number of sessions in the backing session pool.
92+
* Defaults to 100.
93+
* <li>maxSessions (int): Sets the maximum number of sessions in the backing session pool.
94+
* Defaults to 400.
9195
* <li>numChannels (int): Sets the number of gRPC channels to use. Defaults to 4.
9296
* <li>usePlainText (boolean): Sets whether the JDBC connection should establish an unencrypted
9397
* connection to the server. This option can only be used when connecting to a local emulator
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright 2021 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.spanner.jdbc;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
21+
import com.google.cloud.spanner.connection.AbstractMockServerTest;
22+
import com.google.common.base.Predicate;
23+
import com.google.protobuf.AbstractMessage;
24+
import com.google.spanner.v1.BatchCreateSessionsRequest;
25+
import java.sql.Connection;
26+
import java.sql.SQLException;
27+
import java.util.concurrent.Callable;
28+
import java.util.concurrent.CountDownLatch;
29+
import java.util.concurrent.ExecutionException;
30+
import java.util.concurrent.ExecutorService;
31+
import java.util.concurrent.Executors;
32+
import java.util.concurrent.Future;
33+
import java.util.concurrent.TimeUnit;
34+
import java.util.concurrent.TimeoutException;
35+
import org.junit.AfterClass;
36+
import org.junit.Test;
37+
import org.junit.experimental.runners.Enclosed;
38+
import org.junit.runner.RunWith;
39+
40+
@RunWith(Enclosed.class)
41+
public class JdbcConnectionUrlTest {
42+
43+
public static class ConnectionMinSessionsTest extends AbstractMockServerTest {
44+
@AfterClass
45+
public static void reset() {
46+
mockSpanner.reset();
47+
}
48+
49+
protected String getBaseUrl() {
50+
return super.getBaseUrl() + ";minSessions=1";
51+
}
52+
53+
@Test
54+
public void testMinSessions() throws InterruptedException, TimeoutException, SQLException {
55+
try (Connection connection = createJdbcConnection()) {
56+
mockSpanner.waitForRequestsToContain(
57+
new Predicate<AbstractMessage>() {
58+
@Override
59+
public boolean apply(AbstractMessage input) {
60+
return input instanceof BatchCreateSessionsRequest
61+
&& ((BatchCreateSessionsRequest) input).getSessionCount() == 1;
62+
}
63+
},
64+
5000L);
65+
}
66+
}
67+
}
68+
69+
public static class ConnectionMaxSessionsTest extends AbstractMockServerTest {
70+
71+
@AfterClass
72+
public static void reset() {
73+
mockSpanner.reset();
74+
}
75+
76+
protected String getBaseUrl() {
77+
return super.getBaseUrl() + ";maxSessions=1";
78+
}
79+
80+
@Test
81+
public void testMaxSessions()
82+
throws InterruptedException, TimeoutException, ExecutionException, SQLException {
83+
ExecutorService executor1 = Executors.newSingleThreadExecutor();
84+
ExecutorService executor2 = Executors.newSingleThreadExecutor();
85+
86+
try (Connection connection1 = createJdbcConnection();
87+
Connection connection2 = createJdbcConnection()) {
88+
final CountDownLatch latch = new CountDownLatch(1);
89+
Future<Void> fut1 =
90+
executor1.submit(
91+
new Callable<Void>() {
92+
@Override
93+
public Void call() throws SQLException, InterruptedException {
94+
latch.await(5L, TimeUnit.SECONDS);
95+
connection1.createStatement().executeUpdate(INSERT_STATEMENT.getSql());
96+
connection1.commit();
97+
return null;
98+
}
99+
});
100+
Future<Void> fut2 =
101+
executor2.submit(
102+
new Callable<Void>() {
103+
@Override
104+
public Void call() throws SQLException {
105+
latch.countDown();
106+
connection2.createStatement().executeUpdate(INSERT_STATEMENT.getSql());
107+
connection2.commit();
108+
return null;
109+
}
110+
});
111+
// Wait until both finishes.
112+
fut1.get(5L, TimeUnit.SECONDS);
113+
fut2.get(5L, TimeUnit.SECONDS);
114+
} finally {
115+
executor1.shutdown();
116+
executor2.shutdown();
117+
}
118+
assertThat(mockSpanner.numSessionsCreated()).isEqualTo(1);
119+
}
120+
}
121+
}

src/test/java/com/google/cloud/spanner/jdbc/JdbcGrpcErrorTest.java

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
import org.junit.After;
4747
import org.junit.AfterClass;
4848
import org.junit.BeforeClass;
49-
import org.junit.Ignore;
5049
import org.junit.Test;
5150
import org.junit.runner.RunWith;
5251
import org.junit.runners.JUnit4;
@@ -133,30 +132,28 @@ public void reset() {
133132

134133
private String createUrl() {
135134
return String.format(
136-
"jdbc:cloudspanner://localhost:%d/projects/%s/instances/%s/databases/%s?usePlainText=true",
135+
"jdbc:cloudspanner://localhost:%d/projects/%s/instances/%s/databases/%s?usePlainText=true;minSessions=0",
137136
server.getPort(), "proj", "inst", "db");
138137
}
139138

140139
private Connection createConnection() throws SQLException {
141140
return DriverManager.getConnection(createUrl());
142141
}
143142

144-
@Ignore(
145-
"This can only be guaranteed with MinSessions=0. Re-enable when MinSessions is configurable for JDBC.")
146143
@Test
147144
public void autocommitBeginTransaction() {
148145
mockSpanner.setBeginTransactionExecutionTime(
149146
SimulatedExecutionTime.ofException(serverException));
150147
try (java.sql.Connection connection = createConnection()) {
148+
// This triggers a retry with an explicit BeginTransaction RPC.
149+
mockSpanner.abortNextStatement();
151150
connection.createStatement().executeUpdate(UPDATE_STATEMENT.getSql());
152151
fail("missing expected exception");
153152
} catch (SQLException e) {
154153
assertThat(testExceptionMatcher.matches(e)).isTrue();
155154
}
156155
}
157156

158-
@Ignore(
159-
"This can only be guaranteed with MinSessions=0. Re-enable when MinSessions is configurable for JDBC.")
160157
@Test
161158
public void autocommitBeginPDMLTransaction() {
162159
mockSpanner.setBeginTransactionExecutionTime(
@@ -170,23 +167,21 @@ public void autocommitBeginPDMLTransaction() {
170167
}
171168
}
172169

173-
@Ignore(
174-
"This can only be guaranteed with MinSessions=0. Re-enable when MinSessions is configurable for JDBC.")
175170
@Test
176171
public void transactionalBeginTransaction() {
177172
mockSpanner.setBeginTransactionExecutionTime(
178173
SimulatedExecutionTime.ofException(serverException));
179174
try (java.sql.Connection connection = createConnection()) {
180175
connection.setAutoCommit(false);
176+
// This triggers a retry with an explicit BeginTransaction RPC.
177+
mockSpanner.abortNextStatement();
181178
connection.createStatement().executeUpdate(UPDATE_STATEMENT.getSql());
182179
fail("missing expected exception");
183180
} catch (SQLException e) {
184181
assertThat(testExceptionMatcher.matches(e)).isTrue();
185182
}
186183
}
187184

188-
@Ignore(
189-
"This can only be guaranteed with MinSessions=0. Re-enable when MinSessions is configurable for JDBC.")
190185
@Test
191186
public void readOnlyBeginTransaction() {
192187
mockSpanner.setBeginTransactionExecutionTime(
@@ -368,8 +363,6 @@ public void readOnlyExecuteStreamingSql() {
368363
}
369364
}
370365

371-
@Ignore(
372-
"This can only be guaranteed with MinSessions=0. Re-enable when MinSessions is configurable for JDBC.")
373366
@Test
374367
public void autocommitCreateSession() {
375368
mockSpanner.setBatchCreateSessionsExecutionTime(
@@ -382,8 +375,6 @@ public void autocommitCreateSession() {
382375
}
383376
}
384377

385-
@Ignore(
386-
"This can only be guaranteed with MinSessions=0. Re-enable when MinSessions is configurable for JDBC.")
387378
@Test
388379
public void transactionalCreateSession() {
389380
mockSpanner.setBatchCreateSessionsExecutionTime(
@@ -397,8 +388,6 @@ public void transactionalCreateSession() {
397388
}
398389
}
399390

400-
@Ignore(
401-
"This can only be guaranteed with MinSessions=0. Re-enable when MinSessions is configurable for JDBC.")
402391
@Test
403392
public void readOnlyCreateSession() {
404393
mockSpanner.setBatchCreateSessionsExecutionTime(

0 commit comments

Comments
 (0)