Skip to content

Commit 5276c5e

Browse files
committed
refactor: add internal data structures for transactional connection state
This change adds internal data structures that can be used for transactional connection state. These data structures also reduces the amount of code that is needed for each connection property that is added. Connection properties are currently represented as actual variables in the ConnectionImpl class. These new data structures removes the need for that. Only the connection property retryAbortsInternally is refactored to use the new data structure. All other connection properties will be refactored in a following change, in order to keep each change as small as possible. The data structure supports both transactional and non-transactional connection state. Transactional state is disabled in the current version in order to be consistent with the current behavior. It will be enabled in a later change when all connection properties have been refactored to use the new data structure.
1 parent 6004c9f commit 5276c5e

File tree

12 files changed

+1432
-28
lines changed

12 files changed

+1432
-28
lines changed

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,11 @@ private E get(String value) {
7272

7373
/** Converter from string to {@link Boolean} */
7474
static class BooleanConverter implements ClientSideStatementValueConverter<Boolean> {
75+
static final BooleanConverter INSTANCE = new BooleanConverter();
7576

77+
private BooleanConverter() {}
78+
79+
/** Constructor that is needed for reflection. */
7680
public BooleanConverter(String allowedValues) {}
7781

7882
@Override
@@ -141,7 +145,11 @@ public Boolean convert(String value) {
141145

142146
/** Converter from string to a non-negative integer. */
143147
static class NonNegativeIntegerConverter implements ClientSideStatementValueConverter<Integer> {
148+
static final NonNegativeIntegerConverter INSTANCE = new NonNegativeIntegerConverter();
149+
150+
private NonNegativeIntegerConverter() {}
144151

152+
/** Constructor needed for reflection. */
145153
public NonNegativeIntegerConverter(String allowedValues) {}
146154

147155
@Override
@@ -356,9 +364,14 @@ public DirectedReadOptions convert(String value) {
356364
/** Converter for converting strings to {@link AutocommitDmlMode} values. */
357365
static class AutocommitDmlModeConverter
358366
implements ClientSideStatementValueConverter<AutocommitDmlMode> {
367+
static final AutocommitDmlModeConverter INSTANCE = new AutocommitDmlModeConverter();
368+
359369
private final CaseInsensitiveEnumMap<AutocommitDmlMode> values =
360370
new CaseInsensitiveEnumMap<>(AutocommitDmlMode.class);
361371

372+
private AutocommitDmlModeConverter() {}
373+
374+
/** Constructor needed for reflection. */
362375
public AutocommitDmlModeConverter(String allowedValues) {}
363376

364377
@Override
@@ -372,7 +385,35 @@ public AutocommitDmlMode convert(String value) {
372385
}
373386
}
374387

388+
static class ConnectionStateTypeConverter
389+
implements ClientSideStatementValueConverter<ConnectionState.Type> {
390+
static final ConnectionStateTypeConverter INSTANCE = new ConnectionStateTypeConverter();
391+
392+
private final CaseInsensitiveEnumMap<ConnectionState.Type> values =
393+
new CaseInsensitiveEnumMap<>(ConnectionState.Type.class);
394+
395+
private ConnectionStateTypeConverter() {}
396+
397+
/** Constructor that is needed for reflection. */
398+
public ConnectionStateTypeConverter(String allowedValues) {}
399+
400+
@Override
401+
public Class<ConnectionState.Type> getParameterClass() {
402+
return ConnectionState.Type.class;
403+
}
404+
405+
@Override
406+
public ConnectionState.Type convert(String value) {
407+
return values.get(value);
408+
}
409+
}
410+
375411
static class StringValueConverter implements ClientSideStatementValueConverter<String> {
412+
static final StringValueConverter INSTANCE = new StringValueConverter();
413+
414+
private StringValueConverter() {}
415+
416+
/** Constructor needed for reflection. */
376417
public StringValueConverter(String allowedValues) {}
377418

378419
@Override

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

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import static com.google.cloud.spanner.SpannerApiFutures.get;
2020
import static com.google.cloud.spanner.connection.ConnectionPreconditions.checkValidIdentifier;
21+
import static com.google.cloud.spanner.connection.ConnectionProperties.RETRY_ABORTS_INTERNALLY;
2122

2223
import com.google.api.core.ApiFuture;
2324
import com.google.api.core.ApiFutures;
@@ -50,6 +51,7 @@
5051
import com.google.cloud.spanner.TimestampBound.Mode;
5152
import com.google.cloud.spanner.connection.AbstractStatementParser.ParsedStatement;
5253
import com.google.cloud.spanner.connection.AbstractStatementParser.StatementType;
54+
import com.google.cloud.spanner.connection.ConnectionProperty.Context;
5355
import com.google.cloud.spanner.connection.StatementExecutor.StatementTimeout;
5456
import com.google.cloud.spanner.connection.StatementResult.ResultType;
5557
import com.google.cloud.spanner.connection.UnitOfWork.CallType;
@@ -215,6 +217,7 @@ static UnitOfWorkType of(TransactionMode transactionMode) {
215217
private final DdlClient ddlClient;
216218
private final DatabaseClient dbClient;
217219
private final BatchClient batchClient;
220+
private final ConnectionState connectionState;
218221
private boolean autocommit;
219222
private boolean readOnly;
220223
private boolean returnCommitStats;
@@ -236,7 +239,6 @@ static UnitOfWorkType of(TransactionMode transactionMode) {
236239
private BatchMode batchMode;
237240
private UnitOfWorkType unitOfWorkType;
238241
private final Stack<UnitOfWork> transactionStack = new Stack<>();
239-
private boolean retryAbortsInternally;
240242
private final List<TransactionRetryListener> transactionRetryListeners = new ArrayList<>();
241243
private AutocommitDmlMode autocommitDmlMode = AutocommitDmlMode.TRANSACTIONAL;
242244
private TimestampBound readOnlyStaleness = TimestampBound.strong();
@@ -307,9 +309,10 @@ static UnitOfWorkType of(TransactionMode transactionMode) {
307309
this.dbClient = spanner.getDatabaseClient(options.getDatabaseId());
308310
this.batchClient = spanner.getBatchClient(options.getDatabaseId());
309311
this.ddlClient = createDdlClient();
312+
this.connectionState = new ConnectionState(options.getInitialConnectionPropertyValues());
310313

311314
// (Re)set the state of the connection to the default.
312-
reset();
315+
reset(Context.STARTUP);
313316
}
314317

315318
/** Constructor only for test purposes. */
@@ -334,6 +337,7 @@ static UnitOfWorkType of(TransactionMode transactionMode) {
334337
this.ddlClient = Preconditions.checkNotNull(ddlClient);
335338
this.dbClient = Preconditions.checkNotNull(dbClient);
336339
this.batchClient = Preconditions.checkNotNull(batchClient);
340+
this.connectionState = new ConnectionState(options.getInitialConnectionPropertyValues());
337341
setReadOnly(options.isReadOnly());
338342
setAutocommit(options.isAutocommit());
339343
setReturnCommitStats(options.isReturnCommitStats());
@@ -423,14 +427,22 @@ public ApiFuture<Void> closeAsync() {
423427
return ApiFutures.immediateFuture(null);
424428
}
425429

430+
private Context getCurrentContext() {
431+
return isInTransaction() ? Context.IN_TRANSACTION : Context.OUTSIDE_TRANSACTION;
432+
}
433+
426434
/**
427435
* Resets the state of this connection to the default state in the {@link ConnectionOptions} of
428436
* this connection.
429437
*/
430438
public void reset() {
439+
reset(getCurrentContext());
440+
}
441+
442+
private void reset(Context context) {
431443
ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG);
432444

433-
this.retryAbortsInternally = options.isRetryAbortsInternally();
445+
this.connectionState.resetValue(RETRY_ABORTS_INTERNALLY, context);
434446
this.readOnly = options.isReadOnly();
435447
this.autocommit = options.isAutocommit();
436448
this.queryOptions =
@@ -856,13 +868,14 @@ private void checkSetRetryAbortsInternallyAvailable() {
856868
@Override
857869
public boolean isRetryAbortsInternally() {
858870
ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG);
859-
return retryAbortsInternally;
871+
return this.connectionState.getValue(RETRY_ABORTS_INTERNALLY).getValue();
860872
}
861873

862874
@Override
863875
public void setRetryAbortsInternally(boolean retryAbortsInternally) {
864876
checkSetRetryAbortsInternallyAvailable();
865-
this.retryAbortsInternally = retryAbortsInternally;
877+
this.connectionState.setValue(
878+
RETRY_ABORTS_INTERNALLY, retryAbortsInternally, getCurrentContext());
866879
}
867880

868881
@Override
@@ -1925,7 +1938,8 @@ UnitOfWork createNewUnitOfWork(
19251938
.setDatabaseClient(dbClient)
19261939
.setDelayTransactionStartUntilFirstWrite(delayTransactionStartUntilFirstWrite)
19271940
.setKeepTransactionAlive(keepTransactionAlive)
1928-
.setRetryAbortsInternally(retryAbortsInternally)
1941+
.setRetryAbortsInternally(
1942+
connectionState.getValue(RETRY_ABORTS_INTERNALLY).getValue())
19291943
.setSavepointSupport(savepointSupport)
19301944
.setReturnCommitStats(returnCommitStats)
19311945
.setMaxCommitDelay(maxCommitDelay)

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

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package com.google.cloud.spanner.connection;
1818

19+
import static com.google.cloud.spanner.connection.ConnectionProperties.RETRY_ABORTS_INTERNALLY;
20+
1921
import com.google.api.core.InternalApi;
2022
import com.google.api.gax.core.CredentialsProvider;
2123
import com.google.api.gax.rpc.TransportChannelProvider;
@@ -37,6 +39,7 @@
3739
import com.google.common.annotations.VisibleForTesting;
3840
import com.google.common.base.Preconditions;
3941
import com.google.common.base.Strings;
42+
import com.google.common.collect.ImmutableMap;
4043
import com.google.common.collect.Sets;
4144
import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions;
4245
import io.opentelemetry.api.OpenTelemetry;
@@ -48,6 +51,7 @@
4851
import java.util.ArrayList;
4952
import java.util.Arrays;
5053
import java.util.Collections;
54+
import java.util.HashMap;
5155
import java.util.HashSet;
5256
import java.util.List;
5357
import java.util.Map;
@@ -529,6 +533,8 @@ public interface ExternalChannelProvider {
529533

530534
/** Builder for {@link ConnectionOptions} instances. */
531535
public static class Builder {
536+
private final Map<String, ConnectionPropertyValue<?>> connectionPropertyValues =
537+
new HashMap<>();
532538
private String uri;
533539
private String credentialsUrl;
534540
private String oauthToken;
@@ -626,6 +632,13 @@ public Builder setUri(String uri) {
626632
return this;
627633
}
628634

635+
<T> Builder setConnectionPropertyValue(
636+
com.google.cloud.spanner.connection.ConnectionProperty<T> property, T value) {
637+
this.connectionPropertyValues.put(
638+
property.getKey(), new ConnectionPropertyValue<>(property, value, value));
639+
return this;
640+
}
641+
629642
/** Sets the {@link SessionPoolOptions} to use for the connection. */
630643
public Builder setSessionPoolOptions(SessionPoolOptions sessionPoolOptions) {
631644
Preconditions.checkNotNull(sessionPoolOptions);
@@ -715,6 +728,7 @@ public static Builder newBuilder() {
715728
return new Builder();
716729
}
717730

731+
private final ConnectionState initialConnectionState;
718732
private final String uri;
719733
private final String warnings;
720734
private final String credentialsUrl;
@@ -756,7 +770,6 @@ public static Builder newBuilder() {
756770
private final boolean autocommit;
757771
private final boolean readOnly;
758772
private final boolean routeToLeader;
759-
private final boolean retryAbortsInternally;
760773
private final boolean useVirtualThreads;
761774
private final boolean useVirtualGrpcTransportThreads;
762775
private final OpenTelemetry openTelemetry;
@@ -773,6 +786,11 @@ private ConnectionOptions(Builder builder) {
773786
this.warnings = checkValidProperties(builder.uri);
774787

775788
this.uri = builder.uri;
789+
ImmutableMap<String, ConnectionPropertyValue<?>> connectionPropertyValues =
790+
ImmutableMap.<String, ConnectionPropertyValue<?>>builder()
791+
.putAll(ConnectionProperties.parseValues(builder.uri))
792+
.putAll(builder.connectionPropertyValues)
793+
.buildKeepingLast();
776794
this.credentialsUrl =
777795
builder.credentialsUrl != null ? builder.credentialsUrl : parseCredentials(builder.uri);
778796
this.encodedCredentials = parseEncodedCredentials(builder.uri);
@@ -866,7 +884,6 @@ private ConnectionOptions(Builder builder) {
866884
this.autocommit = parseAutocommit(this.uri);
867885
this.readOnly = parseReadOnly(this.uri);
868886
this.routeToLeader = parseRouteToLeader(this.uri);
869-
this.retryAbortsInternally = parseRetryAbortsInternally(this.uri);
870887
this.useVirtualThreads = parseUseVirtualThreads(this.uri);
871888
this.useVirtualGrpcTransportThreads = parseUseVirtualGrpcTransportThreads(this.uri);
872889
this.openTelemetry = builder.openTelemetry;
@@ -896,6 +913,7 @@ private ConnectionOptions(Builder builder) {
896913
} else {
897914
this.sessionPoolOptions = SessionPoolOptions.newBuilder().setAutoDetectDialect(true).build();
898915
}
916+
this.initialConnectionState = new ConnectionState(connectionPropertyValues);
899917
}
900918

901919
@VisibleForTesting
@@ -993,12 +1011,6 @@ static boolean parseRouteToLeader(String uri) {
9931011
return value != null ? Boolean.parseBoolean(value) : DEFAULT_ROUTE_TO_LEADER;
9941012
}
9951013

996-
@VisibleForTesting
997-
static boolean parseRetryAbortsInternally(String uri) {
998-
String value = parseUriProperty(uri, RETRY_ABORTS_INTERNALLY_PROPERTY_NAME);
999-
return value != null ? Boolean.parseBoolean(value) : DEFAULT_RETRY_ABORTS_INTERNALLY;
1000-
}
1001-
10021014
@VisibleForTesting
10031015
static boolean parseUseVirtualThreads(String uri) {
10041016
String value = parseUriProperty(uri, USE_VIRTUAL_THREADS_PROPERTY_NAME);
@@ -1357,6 +1369,11 @@ public String getUri() {
13571369
return uri;
13581370
}
13591371

1372+
/** The connection properties that have been pre-set for this {@link ConnectionOptions}. */
1373+
Map<String, ConnectionPropertyValue<?>> getInitialConnectionPropertyValues() {
1374+
return this.initialConnectionState.getAllValues();
1375+
}
1376+
13601377
/** The credentials URL of this {@link ConnectionOptions} */
13611378
public String getCredentialsUrl() {
13621379
return credentialsUrl;
@@ -1488,7 +1505,7 @@ public boolean isRouteToLeader() {
14881505
* ConnectionOptions}
14891506
*/
14901507
public boolean isRetryAbortsInternally() {
1491-
return retryAbortsInternally;
1508+
return this.initialConnectionState.getValue(RETRY_ABORTS_INTERNALLY).getValue();
14921509
}
14931510

14941511
/** Whether connections should use virtual threads for connection executors. */

0 commit comments

Comments
 (0)