Skip to content

chore: make valid connection properties public #3546

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -112,27 +112,30 @@
import com.google.cloud.spanner.connection.ClientSideStatementValueConverters.StringValueConverter;
import com.google.cloud.spanner.connection.ConnectionProperty.Context;
import com.google.cloud.spanner.connection.DirectedReadOptionsUtil.DirectedReadOptionsConverter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.spanner.v1.DirectedReadOptions;
import java.time.Duration;
import java.util.Map;

/**
* Utility class that defines all known connection properties. This class will eventually replace
* the list of {@link com.google.cloud.spanner.connection.ConnectionOptions.ConnectionProperty} in
* {@link ConnectionOptions}.
*/
class ConnectionProperties {
public class ConnectionProperties {
private static final ImmutableMap.Builder<String, ConnectionProperty<?>>
CONNECTION_PROPERTIES_BUILDER = ImmutableMap.builder();

private static final Boolean[] BOOLEANS = new Boolean[] {Boolean.TRUE, Boolean.FALSE};

static final ConnectionProperty<ConnectionState.Type> CONNECTION_STATE_TYPE =
create(
"connection_state_type",
"The type of connection state to use for this connection. Can only be set at start up. "
+ "If no value is set, then the database dialect default will be used, "
+ "which is NON_TRANSACTIONAL for GoogleSQL and TRANSACTIONAL for PostgreSQL.",
null,
ConnectionState.Type.values(),
ConnectionStateTypeConverter.INSTANCE,
Context.STARTUP);
static final ConnectionProperty<String> TRACING_PREFIX =
Expand All @@ -148,6 +151,7 @@ class ConnectionProperties {
LENIENT_PROPERTY_NAME,
"Silently ignore unknown properties in the connection string/properties (true/false)",
DEFAULT_LENIENT,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.STARTUP);
static final ConnectionProperty<String> ENDPOINT =
Expand All @@ -167,6 +171,7 @@ class ConnectionProperties {
+ "The instance and database in the connection string will automatically be created if these do not yet exist on the emulator. "
+ "Add dialect=postgresql to the connection string to make sure that the database that is created uses the PostgreSQL dialect.",
false,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.STARTUP);
static final ConnectionProperty<Boolean> USE_AUTO_SAVEPOINTS_FOR_EMULATOR =
Expand All @@ -175,13 +180,15 @@ class ConnectionProperties {
"Automatically creates savepoints for each statement in a read/write transaction when using the Emulator. "
+ "This is no longer needed when using Emulator version 1.5.23 or higher.",
false,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.STARTUP);
static final ConnectionProperty<Boolean> USE_PLAIN_TEXT =
create(
USE_PLAIN_TEXT_PROPERTY_NAME,
"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.",
DEFAULT_USE_PLAIN_TEXT,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.STARTUP);

Expand Down Expand Up @@ -226,6 +233,7 @@ class ConnectionProperties {
DIALECT_PROPERTY_NAME,
"Sets the dialect to use for new databases that are created by this connection.",
Dialect.GOOGLE_STANDARD_SQL,
Dialect.values(),
DialectConverter.INSTANCE,
Context.STARTUP);
static final ConnectionProperty<Boolean> TRACK_SESSION_LEAKS =
Expand All @@ -238,6 +246,7 @@ class ConnectionProperties {
+ "actual session leak is detected. The stack trace of the exception will "
+ "in that case not contain the call stack of when the session was checked out.",
DEFAULT_TRACK_SESSION_LEAKS,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.STARTUP);
static final ConnectionProperty<Boolean> TRACK_CONNECTION_LEAKS =
Expand All @@ -250,13 +259,15 @@ class ConnectionProperties {
+ "actual connection leak is detected. The stack trace of the exception will "
+ "in that case not contain the call stack of when the connection was created.",
DEFAULT_TRACK_CONNECTION_LEAKS,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.STARTUP);
static final ConnectionProperty<Boolean> ROUTE_TO_LEADER =
create(
ROUTE_TO_LEADER_PROPERTY_NAME,
"Should read/write transactions and partitioned DML be routed to leader region (true/false)",
DEFAULT_ROUTE_TO_LEADER,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.STARTUP);
static final ConnectionProperty<Boolean> USE_VIRTUAL_THREADS =
Expand All @@ -265,6 +276,7 @@ class ConnectionProperties {
"Use a virtual thread instead of a platform thread for each connection (true/false). "
+ "This option only has any effect if the application is running on Java 21 or higher. In all other cases, the option is ignored.",
DEFAULT_USE_VIRTUAL_THREADS,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.STARTUP);
static final ConnectionProperty<Boolean> USE_VIRTUAL_GRPC_TRANSPORT_THREADS =
Expand All @@ -273,6 +285,7 @@ class ConnectionProperties {
"Use a virtual thread instead of a platform thread for the gRPC executor (true/false). "
+ "This option only has any effect if the application is running on Java 21 or higher. In all other cases, the option is ignored.",
DEFAULT_USE_VIRTUAL_GRPC_TRANSPORT_THREADS,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.STARTUP);
static final ConnectionProperty<Boolean> ENABLE_EXTENDED_TRACING =
Expand All @@ -282,6 +295,7 @@ class ConnectionProperties {
+ "by this connection. The SQL string is added as the standard OpenTelemetry "
+ "attribute 'db.statement'.",
DEFAULT_ENABLE_EXTENDED_TRACING,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.STARTUP);
static final ConnectionProperty<Boolean> ENABLE_API_TRACING =
Expand All @@ -292,6 +306,7 @@ class ConnectionProperties {
+ "or if you want to debug potential latency problems caused by RPCs that are "
+ "being retried.",
DEFAULT_ENABLE_API_TRACING,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.STARTUP);
static final ConnectionProperty<Boolean> ENABLE_END_TO_END_TRACING =
Expand All @@ -302,6 +317,7 @@ class ConnectionProperties {
+ "Server side traces can only go to Google Cloud Trace, so to see end to end traces, "
+ "the application should configure an exporter that exports the traces to Google Cloud Trace.",
DEFAULT_ENABLE_END_TO_END_TRACING,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.STARTUP);
static final ConnectionProperty<Integer> MIN_SESSIONS =
Expand Down Expand Up @@ -345,20 +361,24 @@ class ConnectionProperties {
AUTOCOMMIT_PROPERTY_NAME,
"Should the connection start in autocommit (true/false)",
DEFAULT_AUTOCOMMIT,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.USER);
static final ConnectionProperty<Boolean> READONLY =
create(
READONLY_PROPERTY_NAME,
"Should the connection start in read-only mode (true/false)",
DEFAULT_READONLY,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.USER);
static final ConnectionProperty<AutocommitDmlMode> AUTOCOMMIT_DML_MODE =
create(
"autocommit_dml_mode",
"Should the connection automatically retry Aborted errors (true/false)",
"Determines the transaction type that is used to execute "
+ "DML statements when the connection is in auto-commit mode.",
AutocommitDmlMode.TRANSACTIONAL,
AutocommitDmlMode.values(),
AutocommitDmlModeConverter.INSTANCE,
Context.USER);
static final ConnectionProperty<Boolean> RETRY_ABORTS_INTERNALLY =
Expand All @@ -371,13 +391,15 @@ class ConnectionProperties {
RETRY_ABORTS_INTERNALLY_PROPERTY_NAME,
"Should the connection automatically retry Aborted errors (true/false)",
DEFAULT_RETRY_ABORTS_INTERNALLY,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.USER);
static final ConnectionProperty<Boolean> RETURN_COMMIT_STATS =
create(
"returnCommitStats",
"Request that Spanner returns commit statistics for read/write transactions (true/false)",
DEFAULT_RETURN_COMMIT_STATS,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.USER);
static final ConnectionProperty<Boolean> DELAY_TRANSACTION_START_UNTIL_FIRST_WRITE =
Expand All @@ -389,6 +411,7 @@ class ConnectionProperties {
+ "the first write operation in a read/write transaction will be executed using the read/write transaction. Enabling this mode can reduce locking "
+ "and improve performance for applications that can handle the lower transaction isolation semantics.",
DEFAULT_DELAY_TRANSACTION_START_UNTIL_FIRST_WRITE,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.USER);
static final ConnectionProperty<Boolean> KEEP_TRANSACTION_ALIVE =
Expand All @@ -398,6 +421,7 @@ class ConnectionProperties {
+ "if no other statements are being executed. This option should be used with caution, as it can keep transactions alive and hold on to locks "
+ "longer than intended. This option should typically be used for CLI-type application that might wait for user input for a longer period of time.",
DEFAULT_KEEP_TRANSACTION_ALIVE,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.USER);

Expand All @@ -415,6 +439,7 @@ class ConnectionProperties {
+ "Executing a query that cannot be partitioned will fail. "
+ "Executing a query in a read/write transaction will also fail.",
DEFAULT_AUTO_PARTITION_MODE,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.USER);
static final ConnectionProperty<Boolean> DATA_BOOST_ENABLED =
Expand All @@ -423,6 +448,7 @@ class ConnectionProperties {
"Enable data boost for all partitioned queries that are executed by this connection. "
+ "This setting is only used for partitioned queries and is ignored by all other statements.",
DEFAULT_DATA_BOOST_ENABLED,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.USER);
static final ConnectionProperty<Integer> MAX_PARTITIONS =
Expand Down Expand Up @@ -468,20 +494,23 @@ class ConnectionProperties {
RPC_PRIORITY_NAME,
"Sets the priority for all RPC invocations from this connection (HIGH/MEDIUM/LOW). The default is HIGH.",
DEFAULT_RPC_PRIORITY,
RpcPriority.values(),
RpcPriorityConverter.INSTANCE,
Context.USER);
static final ConnectionProperty<SavepointSupport> SAVEPOINT_SUPPORT =
create(
"savepoint_support",
"Determines the behavior of the connection when savepoints are used.",
SavepointSupport.FAIL_AFTER_ROLLBACK,
SavepointSupport.values(),
SavepointSupportConverter.INSTANCE,
Context.USER);
static final ConnectionProperty<DdlInTransactionMode> DDL_IN_TRANSACTION_MODE =
create(
DDL_IN_TRANSACTION_MODE_PROPERTY_NAME,
"Determines how the connection should handle DDL statements in a read/write transaction.",
DEFAULT_DDL_IN_TRANSACTION_MODE,
DdlInTransactionMode.values(),
DdlInTransactionModeConverter.INSTANCE,
Context.USER);
static final ConnectionProperty<Duration> MAX_COMMIT_DELAY =
Expand All @@ -504,6 +533,7 @@ class ConnectionProperties {
+ "This setting is only in read/write transactions. DML statements in auto-commit mode "
+ "are executed directly.",
DEFAULT_AUTO_BATCH_DML,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.USER);
static final ConnectionProperty<Long> AUTO_BATCH_DML_UPDATE_COUNT =
Expand Down Expand Up @@ -538,23 +568,45 @@ class ConnectionProperties {
+ AUTO_BATCH_DML_UPDATE_COUNT_VERIFICATION_PROPERTY_NAME
+ " to false.",
DEFAULT_AUTO_BATCH_DML_UPDATE_COUNT_VERIFICATION,
BOOLEANS,
BooleanConverter.INSTANCE,
Context.USER);

static final Map<String, ConnectionProperty<?>> CONNECTION_PROPERTIES =
static final ImmutableMap<String, ConnectionProperty<?>> CONNECTION_PROPERTIES =
CONNECTION_PROPERTIES_BUILDER.build();

/** The list of all supported connection properties. */
public static ImmutableList<ConnectionProperty<?>> VALID_CONNECTION_PROPERTIES =
ImmutableList.copyOf(ConnectionProperties.CONNECTION_PROPERTIES.values());

/** Utility method for creating a new core {@link ConnectionProperty}. */
private static <T> ConnectionProperty<T> create(
String name,
String description,
T defaultValue,
ClientSideStatementValueConverter<T> converter,
Context context) {
ConnectionProperty<T> property =
ConnectionProperty.create(name, description, defaultValue, converter, context);
CONNECTION_PROPERTIES_BUILDER.put(property.getKey(), property);
return property;
return create(name, description, defaultValue, null, converter, context);
}

/** Utility method for creating a new core {@link ConnectionProperty}. */
private static <T> ConnectionProperty<T> create(
String name,
String description,
T defaultValue,
T[] validValues,
ClientSideStatementValueConverter<T> converter,
Context context) {
try {
ConnectionProperty<T> property =
ConnectionProperty.create(
name, description, defaultValue, validValues, converter, context);
CONNECTION_PROPERTIES_BUILDER.put(property.getKey(), property);
return property;
} catch (Throwable t) {
t.printStackTrace();
}
return null;
}

/** Parse the connection properties that can be found in the given connection URL. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@
* connection state is an opt-in.
*/
public class ConnectionProperty<T> {

/**
* Context indicates when a {@link ConnectionProperty} may be set. Each higher-ordinal value
* includes the preceding values, meaning that a {@link ConnectionProperty} with {@link
* Context#USER} can be set both at connection startup and during the connection's lifetime.
*/
enum Context {
public enum Context {
/** The property can only be set at startup of the connection. */
STARTUP,
/**
Expand Down Expand Up @@ -79,8 +80,20 @@ static <T> ConnectionProperty<T> create(
T defaultValue,
ClientSideStatementValueConverter<T> converter,
Context context) {
return create(name, description, defaultValue, null, converter, context);
}

/** Utility method for creating a typed {@link ConnectionProperty}. */
@Nonnull
static <T> ConnectionProperty<T> create(
@Nonnull String name,
String description,
T defaultValue,
T[] validValues,
ClientSideStatementValueConverter<T> converter,
Context context) {
return new ConnectionProperty<>(
null, name, description, defaultValue, null, converter, context);
null, name, description, defaultValue, validValues, converter, context);
}

/**
Expand Down Expand Up @@ -163,35 +176,38 @@ ConnectionPropertyValue<T> convert(@Nullable String stringValue) {
return new ConnectionPropertyValue<>(this, convertedValue, convertedValue);
}

String getKey() {
@Nonnull
public String getKey() {
return this.key;
}

boolean hasExtension() {
public boolean hasExtension() {
return this.extension != null;
}

String getExtension() {
public String getExtension() {
return this.extension;
}

String getName() {
@Nonnull
public String getName() {
return this.name;
}

String getDescription() {
@Nonnull
public String getDescription() {
return this.description;
}

T getDefaultValue() {
public T getDefaultValue() {
return this.defaultValue;
}

T[] getValidValues() {
public T[] getValidValues() {
return this.validValues;
}

Context getContext() {
public Context getContext() {
return this.context;
}
}
Loading