Skip to content
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

chore: add property for keep-transaction-alive #3149

Merged
merged 3 commits into from
Jul 3, 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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ implementation 'com.google.cloud:google-cloud-spanner'
If you are using Gradle without BOM, add this to your dependencies:

```Groovy
implementation 'com.google.cloud:google-cloud-spanner:6.69.0'
implementation 'com.google.cloud:google-cloud-spanner:6.70.0'
```

If you are using SBT, add this to your dependencies:

```Scala
libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.69.0"
libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.70.0"
```
<!-- {x-version-update-end} -->

Expand Down Expand Up @@ -687,7 +687,7 @@ Java is a registered trademark of Oracle and/or its affiliates.
[kokoro-badge-link-5]: http://storage.googleapis.com/cloud-devrel-public/java/badges/java-spanner/java11.html
[stability-image]: https://img.shields.io/badge/stability-stable-green
[maven-version-image]: https://img.shields.io/maven-central/v/com.google.cloud/google-cloud-spanner.svg
[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.69.0
[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.70.0
[authentication]: https://github.com/googleapis/google-cloud-java#authentication
[auth-scopes]: https://developers.google.com/identity/protocols/oauth2/scopes
[predefined-iam-roles]: https://cloud.google.com/iam/docs/understanding-roles#predefined_roles
Expand Down
12 changes: 12 additions & 0 deletions google-cloud-spanner/clirr-ignored-differences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -732,4 +732,16 @@
<className>com/google/cloud/spanner/connection/Connection</className>
<method>void reset()</method>
</difference>

<!-- Added keepTransactionAlive property -->
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/connection/Connection</className>
<method>void setKeepTransactionAlive(boolean)</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/connection/Connection</className>
<method>boolean isKeepTransactionAlive()</method>
</difference>
</differences>
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,25 @@ default boolean isDelayTransactionStartUntilFirstWrite() {
throw new UnsupportedOperationException("Unimplemented");
}

/**
* Sets whether this connection should keep read/write transactions alive by executing a SELECT 1
* once every 10 seconds during inactive read/write transactions.
*
* <p>NOTE: This will keep read/write transactions alive and hold on to locks until it is
* explicitly committed or rolled back.
*/
default void setKeepTransactionAlive(boolean keepTransactionAlive) {
throw new UnsupportedOperationException("Unimplemented");
}

/**
* @return true if this connection keeps read/write transactions alive by executing a SELECT 1
* once every 10 seconds during inactive read/write transactions.
*/
default boolean isKeepTransactionAlive() {
throw new UnsupportedOperationException("Unimplemented");
}

/**
* Commits the current transaction of this connection. All mutations that have been buffered
* during the current transaction will be written to the database.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ static UnitOfWorkType of(TransactionMode transactionMode) {
private boolean readOnly;
private boolean returnCommitStats;
private boolean delayTransactionStartUntilFirstWrite;
private boolean keepTransactionAlive;

private UnitOfWork currentUnitOfWork = null;
/**
Expand Down Expand Up @@ -438,6 +439,7 @@ public void reset() {
this.ddlInTransactionMode = options.getDdlInTransactionMode();
this.returnCommitStats = options.isReturnCommitStats();
this.delayTransactionStartUntilFirstWrite = options.isDelayTransactionStartUntilFirstWrite();
this.keepTransactionAlive = options.isKeepTransactionAlive();
this.dataBoostEnabled = options.isDataBoostEnabled();
this.autoPartitionMode = options.isAutoPartitionMode();
this.maxPartitions = options.getMaxPartitions();
Expand Down Expand Up @@ -987,6 +989,20 @@ public boolean isDelayTransactionStartUntilFirstWrite() {
return this.delayTransactionStartUntilFirstWrite;
}

@Override
public void setKeepTransactionAlive(boolean keepTransactionAlive) {
ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG);
ConnectionPreconditions.checkState(
!isTransactionStarted(), "Cannot set KeepTransactionAlive while a transaction is active");
this.keepTransactionAlive = keepTransactionAlive;
}

@Override
public boolean isKeepTransactionAlive() {
ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG);
return this.keepTransactionAlive;
}

/** Resets this connection to its default transaction options. */
private void setDefaultTransactionOptions() {
if (transactionStack.isEmpty()) {
Expand Down Expand Up @@ -1908,6 +1924,7 @@ UnitOfWork createNewUnitOfWork(
.setUseAutoSavepointsForEmulator(options.useAutoSavepointsForEmulator())
.setDatabaseClient(dbClient)
.setDelayTransactionStartUntilFirstWrite(delayTransactionStartUntilFirstWrite)
.setKeepTransactionAlive(keepTransactionAlive)
.setRetryAbortsInternally(retryAbortsInternally)
.setSavepointSupport(savepointSupport)
.setReturnCommitStats(returnCommitStats)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ public String[] getValidValues() {
private static final boolean DEFAULT_LENIENT = false;
private static final boolean DEFAULT_ROUTE_TO_LEADER = true;
private static final boolean DEFAULT_DELAY_TRANSACTION_START_UNTIL_FIRST_WRITE = false;
private static final boolean DEFAULT_KEEP_TRANSACTION_ALIVE = false;
private static final boolean DEFAULT_TRACK_SESSION_LEAKS = true;
private static final boolean DEFAULT_TRACK_CONNECTION_LEAKS = true;
private static final boolean DEFAULT_DATA_BOOST_ENABLED = false;
Expand Down Expand Up @@ -269,6 +270,8 @@ public String[] getValidValues() {
/** Name of the 'delay transaction start until first write' property. */
public static final String DELAY_TRANSACTION_START_UNTIL_FIRST_WRITE_NAME =
"delayTransactionStartUntilFirstWrite";
/** Name of the 'keep transaction alive' property. */
public static final String KEEP_TRANSACTION_ALIVE_PROPERTY_NAME = "keepTransactionAlive";
/** Name of the 'trackStackTraceOfSessionCheckout' connection property. */
public static final String TRACK_SESSION_LEAKS_PROPERTY_NAME = "trackSessionLeaks";
/** Name of the 'trackStackTraceOfConnectionCreation' connection property. */
Expand Down Expand Up @@ -405,6 +408,12 @@ private static String generateGuardedConnectionPropertyError(
+ "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),
ConnectionProperty.createBooleanProperty(
KEEP_TRANSACTION_ALIVE_PROPERTY_NAME,
"Enabling this option will trigger the connection to keep read/write transactions alive by executing a SELECT 1 query once every 10 seconds "
+ "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),
ConnectionProperty.createBooleanProperty(
TRACK_SESSION_LEAKS_PROPERTY_NAME,
"Capture the call stack of the thread that checked out a session of the session pool. This will "
Expand Down Expand Up @@ -735,6 +744,7 @@ public static Builder newBuilder() {
private final RpcPriority rpcPriority;
private final DdlInTransactionMode ddlInTransactionMode;
private final boolean delayTransactionStartUntilFirstWrite;
private final boolean keepTransactionAlive;
private final boolean trackSessionLeaks;
private final boolean trackConnectionLeaks;

Expand Down Expand Up @@ -799,6 +809,7 @@ private ConnectionOptions(Builder builder) {
this.rpcPriority = parseRPCPriority(this.uri);
this.ddlInTransactionMode = parseDdlInTransactionMode(this.uri);
this.delayTransactionStartUntilFirstWrite = parseDelayTransactionStartUntilFirstWrite(this.uri);
this.keepTransactionAlive = parseKeepTransactionAlive(this.uri);
this.trackSessionLeaks = parseTrackSessionLeaks(this.uri);
this.trackConnectionLeaks = parseTrackConnectionLeaks(this.uri);

Expand Down Expand Up @@ -1179,6 +1190,12 @@ static boolean parseDelayTransactionStartUntilFirstWrite(String uri) {
: DEFAULT_DELAY_TRANSACTION_START_UNTIL_FIRST_WRITE;
}

@VisibleForTesting
static boolean parseKeepTransactionAlive(String uri) {
String value = parseUriProperty(uri, KEEP_TRANSACTION_ALIVE_PROPERTY_NAME);
return value != null ? Boolean.parseBoolean(value) : DEFAULT_KEEP_TRANSACTION_ALIVE;
}

@VisibleForTesting
static boolean parseTrackSessionLeaks(String uri) {
String value = parseUriProperty(uri, TRACK_SESSION_LEAKS_PROPERTY_NAME);
Expand Down Expand Up @@ -1315,6 +1332,14 @@ static List<String> parseProperties(String uri) {
return res;
}

static long tryParseLong(String value, long defaultValue) {
try {
return Long.parseLong(value);
} catch (NumberFormatException ignore) {
return defaultValue;
}
}

/**
* Create a new {@link Connection} from this {@link ConnectionOptions}. Calling this method
* multiple times for the same {@link ConnectionOptions} will return multiple instances of {@link
Expand Down Expand Up @@ -1551,6 +1576,18 @@ boolean isDelayTransactionStartUntilFirstWrite() {
return delayTransactionStartUntilFirstWrite;
}

/**
* Whether connections created by this {@link ConnectionOptions} should keep read/write
* transactions alive by executing a SELECT 1 once every 10 seconds if no other statements are
* executed. This option should be used with caution, as enabling it can keep transactions alive
* for a very long time, which will hold on to any locks that have been taken by the transaction.
* This option should typically only be enabled for CLI-type applications or other user-input
* applications that might wait for a longer period of time on user input.
*/
boolean isKeepTransactionAlive() {
return keepTransactionAlive;
}

boolean isTrackConnectionLeaks() {
return this.trackConnectionLeaks;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ StatementResult statementSetDelayTransactionStartUntilFirstWrite(

StatementResult statementShowDelayTransactionStartUntilFirstWrite();

StatementResult statementSetKeepTransactionAlive(Boolean keepTransactionAlive);

StatementResult statementShowKeepTransactionAlive();

StatementResult statementSetStatementTag(String tag);

StatementResult statementShowStatementTag();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_DELAY_TRANSACTION_START_UNTIL_FIRST_WRITE;
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_DIRECTED_READ;
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_EXCLUDE_TXN_FROM_CHANGE_STREAMS;
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_KEEP_TRANSACTION_ALIVE;
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_MAX_COMMIT_DELAY;
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_MAX_PARTITIONED_PARALLELISM;
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_MAX_PARTITIONS;
Expand All @@ -57,6 +58,7 @@
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_DELAY_TRANSACTION_START_UNTIL_FIRST_WRITE;
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_DIRECTED_READ;
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_EXCLUDE_TXN_FROM_CHANGE_STREAMS;
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_KEEP_TRANSACTION_ALIVE;
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_MAX_COMMIT_DELAY;
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_MAX_PARTITIONED_PARALLELISM;
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_MAX_PARTITIONS;
Expand Down Expand Up @@ -388,6 +390,20 @@ public StatementResult statementShowDelayTransactionStartUntilFirstWrite() {
SHOW_DELAY_TRANSACTION_START_UNTIL_FIRST_WRITE);
}

@Override
public StatementResult statementSetKeepTransactionAlive(Boolean keepTransactionAlive) {
getConnection().setKeepTransactionAlive(keepTransactionAlive);
return noResult(SET_KEEP_TRANSACTION_ALIVE);
}

@Override
public StatementResult statementShowKeepTransactionAlive() {
return resultSet(
String.format("%sKEEP_TRANSACTION_ALIVE", getNamespace(connection.getDialect())),
getConnection().isKeepTransactionAlive(),
SHOW_KEEP_TRANSACTION_ALIVE);
}

@Override
public StatementResult statementSetStatementTag(String tag) {
getConnection().setStatementTag("".equals(tag) ? null : tag);
Expand Down
Loading
Loading