Skip to content

Commit 8367bfa

Browse files
authored
chore: enable multplexed sessions for experimental host (#3676)
* chore: enable multplexed sessions for experimental host * pr link added to experimentalApi annotations * max sessions set to 0 for exp host * comments addressed * unit test added * removed comment * changed assert statement
1 parent 42cc961 commit 8367bfa

File tree

8 files changed

+227
-53
lines changed

8 files changed

+227
-53
lines changed

google-cloud-spanner/clirr-ignored-differences.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -822,11 +822,11 @@
822822
<method>java.lang.Object runTransaction(com.google.cloud.spanner.connection.Connection$TransactionCallable)</method>
823823
</difference>
824824

825-
<!-- Added external host option -->
825+
<!-- Added experimental host option -->
826826
<difference>
827827
<differenceType>7012</differenceType>
828828
<className>com/google/cloud/spanner/SpannerOptions$SpannerEnvironment</className>
829-
<method>com.google.auth.oauth2.GoogleCredentials getDefaultExternalHostCredentials()</method>
829+
<method>com.google.auth.oauth2.GoogleCredentials getDefaultExperimentalHostCredentials()</method>
830830
</difference>
831831

832832
<!-- Default sequence kind -->

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

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public class SessionPoolOptions {
3434
private static final int DEFAULT_MAX_SESSIONS = 400;
3535
private static final int DEFAULT_MIN_SESSIONS = 100;
3636
private static final int DEFAULT_INC_STEP = 25;
37+
private static final int EXPERIMENTAL_HOST_REGULAR_SESSIONS = 0;
3738
private static final ActionOnExhaustion DEFAULT_ACTION = ActionOnExhaustion.BLOCK;
3839
private final int minSessions;
3940
private final int maxSessions;
@@ -89,8 +90,12 @@ private SessionPoolOptions(Builder builder) {
8990
// minSessions > maxSessions is only possible if the user has only set a value for maxSessions.
9091
// We allow that to prevent code that only sets a value for maxSessions to break if the
9192
// maxSessions value is less than the default for minSessions.
92-
this.minSessions = Math.min(builder.minSessions, builder.maxSessions);
93-
this.maxSessions = builder.maxSessions;
93+
this.minSessions =
94+
builder.isExperimentalHost
95+
? EXPERIMENTAL_HOST_REGULAR_SESSIONS
96+
: Math.min(builder.minSessions, builder.maxSessions);
97+
this.maxSessions =
98+
builder.isExperimentalHost ? EXPERIMENTAL_HOST_REGULAR_SESSIONS : builder.maxSessions;
9499
this.incStep = builder.incStep;
95100
this.maxIdleSessions = builder.maxIdleSessions;
96101
this.writeSessionsFraction = builder.writeSessionsFraction;
@@ -114,26 +119,30 @@ 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+
|| ((useMultiplexedSessionFromEnvVariable != null)
124+
? useMultiplexedSessionFromEnvVariable
125+
: builder.useMultiplexedSession);
120126
// useMultiplexedSessionForRW priority => Environment var > private setter > client default
121127
Boolean useMultiplexedSessionForRWFromEnvVariable =
122128
getUseMultiplexedSessionForRWFromEnvVariable();
123129
this.useMultiplexedSessionForRW =
124-
(useMultiplexedSessionForRWFromEnvVariable != null)
125-
? useMultiplexedSessionForRWFromEnvVariable
126-
: builder.useMultiplexedSessionForRW;
130+
builder.isExperimentalHost
131+
|| ((useMultiplexedSessionForRWFromEnvVariable != null)
132+
? useMultiplexedSessionForRWFromEnvVariable
133+
: builder.useMultiplexedSessionForRW);
127134
// useMultiplexedSessionPartitionedOps priority => Environment var > private setter > client
128135
// default
129136
Boolean useMultiplexedSessionFromEnvVariablePartitionedOps =
130137
getUseMultiplexedSessionFromEnvVariablePartitionedOps();
131138
this.useMultiplexedSessionForPartitionedOps =
132-
(useMultiplexedSessionFromEnvVariablePartitionedOps != null)
133-
? useMultiplexedSessionFromEnvVariablePartitionedOps
134-
: builder.useMultiplexedSessionPartitionedOps;
139+
builder.isExperimentalHost
140+
|| ((useMultiplexedSessionFromEnvVariablePartitionedOps != null)
141+
? useMultiplexedSessionFromEnvVariablePartitionedOps
142+
: builder.useMultiplexedSessionPartitionedOps);
135143
this.multiplexedSessionMaintenanceDuration = builder.multiplexedSessionMaintenanceDuration;
136-
this.skipVerifyingBeginTransactionForMuxRW = builder.skipVerifyingBeginTransactionForMuxRW;
144+
this.skipVerifyingBeginTransactionForMuxRW =
145+
builder.isExperimentalHost || builder.skipVerifyingBeginTransactionForMuxRW;
137146
}
138147

139148
@Override
@@ -617,6 +626,7 @@ public static class Builder {
617626
private Duration multiplexedSessionMaintenanceDuration = Duration.ofDays(7);
618627
private Clock poolMaintainerClock = Clock.INSTANCE;
619628
private boolean skipVerifyingBeginTransactionForMuxRW = false;
629+
private boolean isExperimentalHost = false;
620630

621631
private static Position getReleaseToPositionFromSystemProperty() {
622632
// NOTE: This System property is a beta feature. Support for it can be removed in the future.
@@ -813,6 +823,12 @@ public Builder setWarnAndCloseIfInactiveTransactions() {
813823
return this;
814824
}
815825

826+
@InternalApi
827+
public Builder setExperimentalHost() {
828+
this.isExperimentalHost = true;
829+
return this;
830+
}
831+
816832
/**
817833
* If there are inactive transactions, release the resources consumed by such transactions. A
818834
* 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: 21 additions & 19 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(
@@ -856,13 +852,13 @@ default String getMonitoringHost() {
856852
return null;
857853
}
858854

859-
default GoogleCredentials getDefaultExternalHostCredentials() {
855+
default GoogleCredentials getDefaultExperimentalHostCredentials() {
860856
return null;
861857
}
862858
}
863859

864-
static final String DEFAULT_SPANNER_EXTERNAL_HOST_CREDENTIALS =
865-
"SPANNER_EXTERNAL_HOST_AUTH_TOKEN";
860+
static final String DEFAULT_SPANNER_EXPERIMENTAL_HOST_CREDENTIALS =
861+
"SPANNER_EXPERIMENTAL_HOST_AUTH_TOKEN";
866862

867863
/**
868864
* Default implementation of {@link SpannerEnvironment}. Reads all configuration from environment
@@ -921,8 +917,8 @@ public String getMonitoringHost() {
921917
}
922918

923919
@Override
924-
public GoogleCredentials getDefaultExternalHostCredentials() {
925-
return getOAuthTokenFromFile(System.getenv(DEFAULT_SPANNER_EXTERNAL_HOST_CREDENTIALS));
920+
public GoogleCredentials getDefaultExperimentalHostCredentials() {
921+
return getOAuthTokenFromFile(System.getenv(DEFAULT_SPANNER_EXPERIMENTAL_HOST_CREDENTIALS));
926922
}
927923
}
928924

@@ -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("https://github.com/googleapis/java-spanner/pull/3676")
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
@@ -1530,7 +1532,7 @@ public Builder setEmulatorHost(String emulatorHost) {
15301532

15311533
/**
15321534
* Configures mTLS authentication using the provided client certificate and key files. mTLS is
1533-
* only supported for external spanner hosts.
1535+
* only supported for experimental spanner hosts.
15341536
*
15351537
* @param clientCertificate Path to the client certificate file.
15361538
* @param clientCertificateKey Path to the client private key file.
@@ -1657,8 +1659,8 @@ 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) {
1661-
credentials = environment.getDefaultExternalHostCredentials();
1662+
} else if (isExperimentalHost && credentials == null) {
1663+
credentials = environment.getDefaultExperimentalHostCredentials();
16621664
}
16631665
if (this.numChannels == null) {
16641666
this.numChannels =
@@ -1700,8 +1702,8 @@ public static void useDefaultEnvironment() {
17001702
}
17011703

17021704
@InternalApi
1703-
public static GoogleCredentials getDefaultExternalHostCredentialsFromSysEnv() {
1704-
return getOAuthTokenFromFile(System.getenv(DEFAULT_SPANNER_EXTERNAL_HOST_CREDENTIALS));
1705+
public static GoogleCredentials getDefaultExperimentalCredentialsFromSysEnv() {
1706+
return getOAuthTokenFromFile(System.getenv(DEFAULT_SPANNER_EXPERIMENTAL_HOST_CREDENTIALS));
17051707
}
17061708

17071709
private static @Nullable GoogleCredentials getOAuthTokenFromFile(@Nullable String file) {

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

Lines changed: 31 additions & 13 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 an 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."),
@@ -664,7 +674,7 @@ private boolean isValidUri(String uri) {
664674
return SPANNER_URI_PATTERN.matcher(uri).matches();
665675
}
666676

667-
private boolean isValidExternalHostUri(String uri) {
677+
private boolean isValidExperimentalHostUri(String uri) {
668678
return EXTERNAL_HOST_PATTERN.matcher(uri).matches();
669679
}
670680

@@ -725,7 +735,7 @@ private boolean isValidExternalHostUri(String uri) {
725735
* @return this builder
726736
*/
727737
public Builder setUri(String uri) {
728-
if (!isValidExternalHostUri(uri)) {
738+
if (!isValidExperimentalHostUri(uri)) {
729739
Preconditions.checkArgument(
730740
isValidUri(uri),
731741
"The specified URI is not a valid Cloud Spanner connection URI. Please specify a URI in the format \"cloudspanner:[//host[:port]]/projects/project-id[/instances/instance-id[/databases/database-name]][\\?property-name=property-value[;property-name=property-value]*]?\"");
@@ -857,10 +867,10 @@ public static Builder newBuilder() {
857867

858868
private ConnectionOptions(Builder builder) {
859869
Matcher matcher;
860-
boolean isExternalHost = false;
861-
if (builder.isValidExternalHostUri(builder.uri)) {
870+
boolean isExperimentalHostPattern = false;
871+
if (builder.isValidExperimentalHostUri(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
}
@@ -923,8 +933,8 @@ private ConnectionOptions(Builder builder) {
923933
getInitialConnectionPropertyValue(AUTO_CONFIG_EMULATOR),
924934
usePlainText,
925935
System.getenv());
926-
GoogleCredentials defaultExternalHostCredentials =
927-
SpannerOptions.getDefaultExternalHostCredentialsFromSysEnv();
936+
GoogleCredentials defaultExperimentalHostCredentials =
937+
SpannerOptions.getDefaultExperimentalCredentialsFromSysEnv();
928938
// Using credentials on a plain text connection is not allowed, so if the user has not specified
929939
// any credentials and is using a plain text connection, we should not try to get the
930940
// credentials from the environment, but default to NoCredentials.
@@ -939,8 +949,9 @@ && getInitialConnectionPropertyValue(OAUTH_TOKEN) == null
939949
this.credentials =
940950
new GoogleCredentials(
941951
new AccessToken(getInitialConnectionPropertyValue(OAUTH_TOKEN), null));
942-
} else if (isExternalHost && defaultExternalHostCredentials != null) {
943-
this.credentials = defaultExternalHostCredentials;
952+
} else if ((isExperimentalHostPattern || isExperimentalHost())
953+
&& defaultExperimentalHostCredentials != null) {
954+
this.credentials = defaultExperimentalHostCredentials;
944955
} else if (getInitialConnectionPropertyValue(CREDENTIALS_PROVIDER) != null) {
945956
try {
946957
this.credentials = getInitialConnectionPropertyValue(CREDENTIALS_PROVIDER).getCredentials();
@@ -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);
992-
} else if (instanceId == null) {
993-
instanceId = "default";
1006+
} else if (instanceId == null && isExperimentalHost()) {
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,

0 commit comments

Comments
 (0)