Skip to content

Commit 0b60e9e

Browse files
committed
chore: enable multplexed sessions for experimental host
1 parent 42cc961 commit 0b60e9e

File tree

6 files changed

+123
-34
lines changed

6 files changed

+123
-34
lines changed

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

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.google.cloud.spanner.SessionPool.Position;
2525
import com.google.common.annotations.VisibleForTesting;
2626
import com.google.common.base.Preconditions;
27+
import io.grpc.ExperimentalApi;
2728
import java.time.Duration;
2829
import java.util.Locale;
2930
import java.util.Objects;
@@ -34,6 +35,7 @@ public class SessionPoolOptions {
3435
private static final int DEFAULT_MAX_SESSIONS = 400;
3536
private static final int DEFAULT_MIN_SESSIONS = 100;
3637
private static final int DEFAULT_INC_STEP = 25;
38+
private static final int EXPERIMENTAL_HOST_MIN_SESSIONS = 0;
3739
private static final ActionOnExhaustion DEFAULT_ACTION = ActionOnExhaustion.BLOCK;
3840
private final int minSessions;
3941
private final int maxSessions;
@@ -89,7 +91,10 @@ private SessionPoolOptions(Builder builder) {
8991
// minSessions > maxSessions is only possible if the user has only set a value for maxSessions.
9092
// We allow that to prevent code that only sets a value for maxSessions to break if the
9193
// maxSessions value is less than the default for minSessions.
92-
this.minSessions = Math.min(builder.minSessions, builder.maxSessions);
94+
this.minSessions =
95+
builder.isExperimentalHost
96+
? EXPERIMENTAL_HOST_MIN_SESSIONS
97+
: Math.min(builder.minSessions, builder.maxSessions);
9398
this.maxSessions = builder.maxSessions;
9499
this.incStep = builder.incStep;
95100
this.maxIdleSessions = builder.maxIdleSessions;
@@ -114,26 +119,33 @@ private SessionPoolOptions(Builder builder) {
114119
// useMultiplexedSession priority => Environment var > private setter > client default
115120
Boolean useMultiplexedSessionFromEnvVariable = getUseMultiplexedSessionFromEnvVariable();
116121
this.useMultiplexedSession =
117-
(useMultiplexedSessionFromEnvVariable != null)
118-
? useMultiplexedSessionFromEnvVariable
119-
: builder.useMultiplexedSession;
122+
builder.isExperimentalHost
123+
? true
124+
: ((useMultiplexedSessionFromEnvVariable != null)
125+
? useMultiplexedSessionFromEnvVariable
126+
: builder.useMultiplexedSession);
120127
// useMultiplexedSessionForRW priority => Environment var > private setter > client default
121128
Boolean useMultiplexedSessionForRWFromEnvVariable =
122129
getUseMultiplexedSessionForRWFromEnvVariable();
123130
this.useMultiplexedSessionForRW =
124-
(useMultiplexedSessionForRWFromEnvVariable != null)
125-
? useMultiplexedSessionForRWFromEnvVariable
126-
: builder.useMultiplexedSessionForRW;
131+
builder.isExperimentalHost
132+
? true
133+
: ((useMultiplexedSessionForRWFromEnvVariable != null)
134+
? useMultiplexedSessionForRWFromEnvVariable
135+
: builder.useMultiplexedSessionForRW);
127136
// useMultiplexedSessionPartitionedOps priority => Environment var > private setter > client
128137
// default
129138
Boolean useMultiplexedSessionFromEnvVariablePartitionedOps =
130139
getUseMultiplexedSessionFromEnvVariablePartitionedOps();
131140
this.useMultiplexedSessionForPartitionedOps =
132-
(useMultiplexedSessionFromEnvVariablePartitionedOps != null)
133-
? useMultiplexedSessionFromEnvVariablePartitionedOps
134-
: builder.useMultiplexedSessionPartitionedOps;
141+
builder.isExperimentalHost
142+
? true
143+
: ((useMultiplexedSessionFromEnvVariablePartitionedOps != null)
144+
? useMultiplexedSessionFromEnvVariablePartitionedOps
145+
: builder.useMultiplexedSessionPartitionedOps);
135146
this.multiplexedSessionMaintenanceDuration = builder.multiplexedSessionMaintenanceDuration;
136-
this.skipVerifyingBeginTransactionForMuxRW = builder.skipVerifyingBeginTransactionForMuxRW;
147+
this.skipVerifyingBeginTransactionForMuxRW =
148+
builder.isExperimentalHost ? true : builder.skipVerifyingBeginTransactionForMuxRW;
137149
}
138150

139151
@Override
@@ -617,6 +629,7 @@ public static class Builder {
617629
private Duration multiplexedSessionMaintenanceDuration = Duration.ofDays(7);
618630
private Clock poolMaintainerClock = Clock.INSTANCE;
619631
private boolean skipVerifyingBeginTransactionForMuxRW = false;
632+
private boolean isExperimentalHost = false;
620633

621634
private static Position getReleaseToPositionFromSystemProperty() {
622635
// NOTE: This System property is a beta feature. Support for it can be removed in the future.
@@ -813,6 +826,12 @@ public Builder setWarnAndCloseIfInactiveTransactions() {
813826
return this;
814827
}
815828

829+
@ExperimentalApi("")
830+
public Builder setExperimentalHost() {
831+
this.isExperimentalHost = true;
832+
return this;
833+
}
834+
816835
/**
817836
* If there are inactive transactions, release the resources consumed by such transactions. A
818837
* transaction is classified as inactive if it executes for more than a system defined duration.

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

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@
9898
import java.util.concurrent.ThreadFactory;
9999
import java.util.concurrent.TimeUnit;
100100
import java.util.concurrent.atomic.AtomicInteger;
101-
import java.util.regex.Pattern;
102101
import javax.annotation.Nonnull;
103102
import javax.annotation.Nullable;
104103
import javax.annotation.concurrent.GuardedBy;
@@ -117,10 +116,7 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
117116

118117
private static final String API_SHORT_NAME = "Spanner";
119118
private static final String DEFAULT_HOST = "https://spanner.googleapis.com";
120-
private static final String CLOUD_SPANNER_HOST_FORMAT = ".*\\.googleapis\\.com.*";
121-
122-
@VisibleForTesting
123-
static final Pattern CLOUD_SPANNER_HOST_PATTERN = Pattern.compile(CLOUD_SPANNER_HOST_FORMAT);
119+
private static final String EXPERIMENTAL_HOST_PROJECT_ID = "default";
124120

125121
private static final ImmutableSet<String> SCOPES =
126122
ImmutableSet.of(
@@ -991,7 +987,7 @@ public static class Builder
991987
private boolean enableBuiltInMetrics = SpannerOptions.environment.isEnableBuiltInMetrics();
992988
private String monitoringHost = SpannerOptions.environment.getMonitoringHost();
993989
private SslContext mTLSContext = null;
994-
private boolean isExternalHost = false;
990+
private boolean isExperimentalHost = false;
995991

996992
private static String createCustomClientLibToken(String token) {
997993
return token + " " + ServiceOptions.getGoogApiClientLibName();
@@ -1484,14 +1480,20 @@ public Builder setDecodeMode(DecodeMode decodeMode) {
14841480
@Override
14851481
public Builder setHost(String host) {
14861482
super.setHost(host);
1487-
if (!CLOUD_SPANNER_HOST_PATTERN.matcher(host).matches()) {
1488-
this.isExternalHost = true;
1489-
}
14901483
// Setting a host should override any SPANNER_EMULATOR_HOST setting.
14911484
setEmulatorHost(null);
14921485
return this;
14931486
}
14941487

1488+
@ExperimentalApi("")
1489+
public Builder setExperimentalHost(String host) {
1490+
super.setHost(host);
1491+
super.setProjectId(EXPERIMENTAL_HOST_PROJECT_ID);
1492+
setSessionPoolOption(SessionPoolOptions.newBuilder().setExperimentalHost().build());
1493+
this.isExperimentalHost = true;
1494+
return this;
1495+
}
1496+
14951497
/**
14961498
* Enables gRPC-GCP extension with the default settings. Do not set
14971499
* GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS to true in combination with this option, as
@@ -1657,7 +1659,7 @@ public SpannerOptions build() {
16571659
this.setChannelConfigurator(ManagedChannelBuilder::usePlaintext);
16581660
// As we are using plain text, we should never send any credentials.
16591661
this.setCredentials(NoCredentials.getInstance());
1660-
} else if (isExternalHost && credentials == null) {
1662+
} else if (isExperimentalHost && credentials == null) {
16611663
credentials = environment.getDefaultExternalHostCredentials();
16621664
}
16631665
if (this.numChannels == null) {

google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import static com.google.cloud.spanner.connection.ConnectionProperties.ENABLE_EXTENDED_TRACING;
3333
import static com.google.cloud.spanner.connection.ConnectionProperties.ENCODED_CREDENTIALS;
3434
import static com.google.cloud.spanner.connection.ConnectionProperties.ENDPOINT;
35+
import static com.google.cloud.spanner.connection.ConnectionProperties.IS_EXPERIMENTAL_HOST;
3536
import static com.google.cloud.spanner.connection.ConnectionProperties.LENIENT;
3637
import static com.google.cloud.spanner.connection.ConnectionProperties.MAX_COMMIT_DELAY;
3738
import static com.google.cloud.spanner.connection.ConnectionProperties.MAX_PARTITIONED_PARALLELISM;
@@ -221,6 +222,7 @@ public String[] getValidValues() {
221222
private static final LocalConnectionChecker LOCAL_CONNECTION_CHECKER =
222223
new LocalConnectionChecker();
223224
static final boolean DEFAULT_USE_PLAIN_TEXT = false;
225+
static final boolean DEFAULT_IS_EXPERIMENTAL_HOST = false;
224226
static final boolean DEFAULT_AUTOCOMMIT = true;
225227
static final boolean DEFAULT_READONLY = false;
226228
static final boolean DEFAULT_RETRY_ABORTS_INTERNALLY = true;
@@ -260,6 +262,8 @@ public String[] getValidValues() {
260262
static final boolean DEFAULT_AUTO_BATCH_DML = false;
261263
static final long DEFAULT_AUTO_BATCH_DML_UPDATE_COUNT = 1L;
262264
static final boolean DEFAULT_AUTO_BATCH_DML_UPDATE_COUNT_VERIFICATION = true;
265+
private static final String EXPERIMENTAL_HOST_PROJECT_ID = "default";
266+
private static final String DEFAULT_EXPERIMENTAL_HOST_INSTANCE_ID = "default";
263267

264268
private static final String PLAIN_TEXT_PROTOCOL = "http:";
265269
private static final String HOST_PROTOCOL = "https:";
@@ -268,6 +272,8 @@ public String[] getValidValues() {
268272
private static final String DEFAULT_EMULATOR_HOST = "http://localhost:9010";
269273
/** Use plain text is only for local testing purposes. */
270274
static final String USE_PLAIN_TEXT_PROPERTY_NAME = "usePlainText";
275+
/** Connect to a Experimental Host * */
276+
static final String IS_EXPERIMENTAL_HOST_PROPERTY_NAME = "isExperimentalHost";
271277
/** Client certificate path to establish mTLS */
272278
static final String CLIENT_CERTIFICATE_PROPERTY_NAME = "clientCertificate";
273279
/** Client key path to establish mTLS */
@@ -444,6 +450,10 @@ static boolean isEnableTransactionalConnectionStateForPostgreSQL() {
444450
USE_PLAIN_TEXT_PROPERTY_NAME,
445451
"Use a plain text communication channel (i.e. non-TLS) for communicating with the server (true/false). Set this value to true for communication with the Cloud Spanner emulator.",
446452
DEFAULT_USE_PLAIN_TEXT),
453+
ConnectionProperty.createBooleanProperty(
454+
IS_EXPERIMENTAL_HOST_PROPERTY_NAME,
455+
"Set this value to true for communication with a Experimental Host.",
456+
DEFAULT_IS_EXPERIMENTAL_HOST),
447457
ConnectionProperty.createStringProperty(
448458
CLIENT_CERTIFICATE_PROPERTY_NAME,
449459
"Specifies the file path to the client certificate required for establishing an mTLS connection."),
@@ -857,10 +867,10 @@ public static Builder newBuilder() {
857867

858868
private ConnectionOptions(Builder builder) {
859869
Matcher matcher;
860-
boolean isExternalHost = false;
870+
boolean isExperimentalHostPattern = false;
861871
if (builder.isValidExternalHostUri(builder.uri)) {
862872
matcher = Builder.EXTERNAL_HOST_PATTERN.matcher(builder.uri);
863-
isExternalHost = true;
873+
isExperimentalHostPattern = true;
864874
} else {
865875
matcher = Builder.SPANNER_URI_PATTERN.matcher(builder.uri);
866876
}
@@ -939,7 +949,8 @@ && getInitialConnectionPropertyValue(OAUTH_TOKEN) == null
939949
this.credentials =
940950
new GoogleCredentials(
941951
new AccessToken(getInitialConnectionPropertyValue(OAUTH_TOKEN), null));
942-
} else if (isExternalHost && defaultExternalHostCredentials != null) {
952+
} else if ((isExperimentalHostPattern || isExperimentalHost())
953+
&& defaultExternalHostCredentials != null) {
943954
this.credentials = defaultExternalHostCredentials;
944955
} else if (getInitialConnectionPropertyValue(CREDENTIALS_PROVIDER) != null) {
945956
try {
@@ -981,16 +992,19 @@ && getInitialConnectionPropertyValue(OAUTH_TOKEN) == null
981992
this.sessionPoolOptions = sessionPoolOptionsBuilder.build();
982993
} else if (builder.sessionPoolOptions != null) {
983994
this.sessionPoolOptions = builder.sessionPoolOptions;
995+
} else if (isExperimentalHostPattern || isExperimentalHost()) {
996+
this.sessionPoolOptions =
997+
SessionPoolOptions.newBuilder().setExperimentalHost().setAutoDetectDialect(true).build();
984998
} else {
985999
this.sessionPoolOptions = SessionPoolOptions.newBuilder().setAutoDetectDialect(true).build();
9861000
}
9871001

988-
String projectId = "default";
1002+
String projectId = EXPERIMENTAL_HOST_PROJECT_ID;
9891003
String instanceId = matcher.group(Builder.INSTANCE_GROUP);
990-
if (!isExternalHost) {
1004+
if (!isExperimentalHost() && !isExperimentalHostPattern) {
9911005
projectId = matcher.group(Builder.PROJECT_GROUP);
9921006
} else if (instanceId == null) {
993-
instanceId = "default";
1007+
instanceId = DEFAULT_EXPERIMENTAL_HOST_INSTANCE_ID;
9941008
}
9951009
if (Builder.DEFAULT_PROJECT_ID_PLACEHOLDER.equalsIgnoreCase(projectId)) {
9961010
projectId = getDefaultProjectId(this.credentials);
@@ -1311,6 +1325,10 @@ boolean isUsePlainText() {
13111325
|| getInitialConnectionPropertyValue(USE_PLAIN_TEXT);
13121326
}
13131327

1328+
boolean isExperimentalHost() {
1329+
return getInitialConnectionPropertyValue(IS_EXPERIMENTAL_HOST);
1330+
}
1331+
13141332
String getClientCertificate() {
13151333
return getInitialConnectionPropertyValue(CLIENT_CERTIFICATE);
13161334
}

google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionProperties.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import static com.google.cloud.spanner.connection.ConnectionOptions.DEFAULT_ENABLE_END_TO_END_TRACING;
4848
import static com.google.cloud.spanner.connection.ConnectionOptions.DEFAULT_ENABLE_EXTENDED_TRACING;
4949
import static com.google.cloud.spanner.connection.ConnectionOptions.DEFAULT_ENDPOINT;
50+
import static com.google.cloud.spanner.connection.ConnectionOptions.DEFAULT_IS_EXPERIMENTAL_HOST;
5051
import static com.google.cloud.spanner.connection.ConnectionOptions.DEFAULT_KEEP_TRANSACTION_ALIVE;
5152
import static com.google.cloud.spanner.connection.ConnectionOptions.DEFAULT_LENIENT;
5253
import static com.google.cloud.spanner.connection.ConnectionOptions.DEFAULT_MAX_PARTITIONED_PARALLELISM;
@@ -76,6 +77,7 @@
7677
import static com.google.cloud.spanner.connection.ConnectionOptions.ENABLE_EXTENDED_TRACING_PROPERTY_NAME;
7778
import static com.google.cloud.spanner.connection.ConnectionOptions.ENCODED_CREDENTIALS_PROPERTY_NAME;
7879
import static com.google.cloud.spanner.connection.ConnectionOptions.ENDPOINT_PROPERTY_NAME;
80+
import static com.google.cloud.spanner.connection.ConnectionOptions.IS_EXPERIMENTAL_HOST_PROPERTY_NAME;
7981
import static com.google.cloud.spanner.connection.ConnectionOptions.KEEP_TRANSACTION_ALIVE_PROPERTY_NAME;
8082
import static com.google.cloud.spanner.connection.ConnectionOptions.LENIENT_PROPERTY_NAME;
8183
import static com.google.cloud.spanner.connection.ConnectionOptions.MAX_PARTITIONED_PARALLELISM_PROPERTY_NAME;
@@ -197,7 +199,14 @@ public class ConnectionProperties {
197199
BOOLEANS,
198200
BooleanConverter.INSTANCE,
199201
Context.STARTUP);
200-
202+
static final ConnectionProperty<Boolean> IS_EXPERIMENTAL_HOST =
203+
create(
204+
IS_EXPERIMENTAL_HOST_PROPERTY_NAME,
205+
"Set this value to true for communication with a Experimental Host.",
206+
DEFAULT_IS_EXPERIMENTAL_HOST,
207+
BOOLEANS,
208+
BooleanConverter.INSTANCE,
209+
Context.STARTUP);
201210
static final ConnectionProperty<String> CLIENT_CERTIFICATE =
202211
create(
203212
CLIENT_CERTIFICATE_PROPERTY_NAME,

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package com.google.cloud.spanner;
1818

19-
import static com.google.cloud.spanner.SpannerOptions.CLOUD_SPANNER_HOST_PATTERN;
2019
import static com.google.common.truth.Truth.assertThat;
2120
import static org.hamcrest.CoreMatchers.is;
2221
import static org.hamcrest.MatcherAssert.assertThat;
@@ -1167,10 +1166,17 @@ public void checkGlobalOpenTelemetryWhenNotInjected() {
11671166
}
11681167

11691168
@Test
1170-
public void testCloudSpannerHostPattern() {
1171-
assertTrue(CLOUD_SPANNER_HOST_PATTERN.matcher("https://spanner.googleapis.com").matches());
1172-
assertTrue(
1173-
CLOUD_SPANNER_HOST_PATTERN.matcher("https://product-area.googleapis.com:443").matches());
1174-
assertFalse(CLOUD_SPANNER_HOST_PATTERN.matcher("https://some-company.com:443").matches());
1169+
public void testExperimentalHostOptions() {
1170+
SpannerOptions options =
1171+
SpannerOptions.newBuilder()
1172+
.setExperimentalHost("localhost:8080")
1173+
.setCredentials(NoCredentials.getInstance())
1174+
.build();
1175+
assertEquals("default", options.getProjectId());
1176+
assertEquals("localhost:8080", options.getHost());
1177+
assertEquals(0, options.getSessionPoolOptions().getMinSessions());
1178+
assertTrue(options.getSessionPoolOptions().getUseMultiplexedSession());
1179+
assertTrue(options.getSessionPoolOptions().getUseMultiplexedSessionForRW());
1180+
assertTrue(options.getSessionPoolOptions().getUseMultiplexedSessionPartitionedOps());
11751181
}
11761182
}

google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1270,4 +1270,39 @@ public void testBuildWithValidURIWithPrefixSpanner() {
12701270
assertThat(options.isAutocommit()).isEqualTo(false);
12711271
assertThat(options.isReadOnly()).isEqualTo(true);
12721272
}
1273+
1274+
@Test
1275+
public void testExperimentalHost() {
1276+
ConnectionOptions.Builder builderWithoutExperimentalHostParam = ConnectionOptions.newBuilder();
1277+
builderWithoutExperimentalHostParam.setUri(
1278+
"spanner://localhost:15000/instances/default/databases/singers-db;usePlainText=true");
1279+
ConnectionOptions optionsWithoutExperimentalHostParam =
1280+
builderWithoutExperimentalHostParam.build();
1281+
assertFalse(optionsWithoutExperimentalHostParam.isExperimentalHost());
1282+
assertEquals(0, optionsWithoutExperimentalHostParam.getSessionPoolOptions().getMinSessions());
1283+
assertTrue(
1284+
optionsWithoutExperimentalHostParam.getSessionPoolOptions().getUseMultiplexedSession());
1285+
assertTrue(
1286+
optionsWithoutExperimentalHostParam
1287+
.getSessionPoolOptions()
1288+
.getUseMultiplexedSessionForRW());
1289+
assertTrue(
1290+
optionsWithoutExperimentalHostParam
1291+
.getSessionPoolOptions()
1292+
.getUseMultiplexedSessionPartitionedOps());
1293+
1294+
ConnectionOptions.Builder builderWithExperimentalHostParam = ConnectionOptions.newBuilder();
1295+
builderWithExperimentalHostParam.setUri(
1296+
"spanner://localhost:15000/projects/default/instances/default/databases/singers-db;usePlainText=true;isExperimentalHost=true");
1297+
ConnectionOptions optionsWithExperimentalHostParam = builderWithExperimentalHostParam.build();
1298+
assertTrue(optionsWithExperimentalHostParam.isExperimentalHost());
1299+
assertEquals(0, optionsWithExperimentalHostParam.getSessionPoolOptions().getMinSessions());
1300+
assertTrue(optionsWithExperimentalHostParam.getSessionPoolOptions().getUseMultiplexedSession());
1301+
assertTrue(
1302+
optionsWithExperimentalHostParam.getSessionPoolOptions().getUseMultiplexedSessionForRW());
1303+
assertTrue(
1304+
optionsWithExperimentalHostParam
1305+
.getSessionPoolOptions()
1306+
.getUseMultiplexedSessionPartitionedOps());
1307+
}
12731308
}

0 commit comments

Comments
 (0)