public interface TransactionContext extends ReadContext
DatabaseClient.write(Iterable)
and DatabaseClient.writeAtLeastOnce(Iterable)
use transactions internally. These transactions rely on
pessimistic locking and, if necessary, two-phase commit. Locking read-write transactions may
abort, requiring the application to retry. However, the interface exposed by TransactionRunner
eliminates the need for applications to write retry loops explicitly.
Locking transactions may be used to atomically read-modify-write data anywhere in a database. This type of transaction is externally consistent.
Clients should attempt to minimize the amount of time a transaction is active. Faster
transactions commit with higher probability and cause less contention. Cloud Spanner attempts to
keep read locks active as long as the transaction continues to do reads, and the transaction has
not been terminated by returning from a TransactionRunner.TransactionCallable
. Long
periods of inactivity at the client may cause Cloud Spanner to release a transaction's locks and
abort it.
Reads performed within a transaction acquire locks on the data being read. Writes can only be done at commit time, after all reads have been completed.
Conceptually, a read-write transaction consists of zero or more reads or SQL queries followed by a commit.
Cloud Spanner can commit the transaction if all read locks it acquired are still valid at
commit time, and it is able to acquire write locks for all writes. Cloud Spanner can abort the
transaction for any reason. If a commit attempt returns ABORTED
, Cloud Spanner guarantees
that the transaction has not modified any user data in Cloud Spanner.
Unless the transaction commits, Cloud Spanner makes no guarantees about how long the transaction's locks were held for. It is an error to use Cloud Spanner locks for any sort of mutual exclusion other than between Cloud Spanner transactions themselves.
When a transaction aborts, the application can choose to retry the whole transaction again. To maximize the chances of successfully committing the retry, the client should execute the retry in the same session as the original attempt. The original session's lock priority increases with each consecutive abort, meaning that each attempt has a slightly better chance of success than the previous.
Under some circumstances (e.g., many transactions attempting to modify the same row(s)), a transaction can abort many times in a short period before successfully committing. Thus, it is not a good idea to cap the number of retries a transaction can attempt; instead, it is better to limit the total amount of wall time spent retrying.
Application code does not need to retry explicitly; TransactionRunner
will
automatically retry a transaction if an attempt results in an abort.
A transaction is considered idle if it has no outstanding reads or SQL queries and has not
started a read or SQL query within the last 10 seconds. Idle transactions can be aborted by Cloud
Spanner so that they don't hold on to locks indefinitely. In that case, the commit will fail with
error ABORTED
.
If this behavior is undesirable, periodically executing a simple SQL query in the transaction
(e.g., SELECT 1
) prevents the transaction from becoming idle.
Session#readWriteTransaction()
,
TransactionRunner
ReadContext.QueryAnalyzeMode
Modifier and Type | Method and Description |
---|---|
default ResultSetStats |
analyzeUpdate(Statement statement,
ReadContext.QueryAnalyzeMode analyzeMode,
Options.UpdateOption... options)
Analyzes a DML statement and returns query plan and/or execution statistics information.
|
long[] |
batchUpdate(Iterable<Statement> statements,
Options.UpdateOption... options)
Executes a list of DML statements in a single request.
|
com.google.api.core.ApiFuture<long[]> |
batchUpdateAsync(Iterable<Statement> statements,
Options.UpdateOption... options)
Same as
#batchUpdate(Iterable) , but is guaranteed to be non-blocking. |
void |
buffer(Iterable<Mutation> mutations)
Buffers mutations to be applied if the transaction commits successfully.
|
void |
buffer(Mutation mutation)
Buffers a single mutation to be applied if the transaction commits successfully.
|
default com.google.api.core.ApiFuture<Void> |
bufferAsync(Iterable<Mutation> mutations)
Same as
buffer(Iterable) , but is guaranteed to be non-blocking. |
default com.google.api.core.ApiFuture<Void> |
bufferAsync(Mutation mutation)
Same as
buffer(Mutation) , but is guaranteed to be non-blocking. |
long |
executeUpdate(Statement statement,
Options.UpdateOption... options)
Executes the DML statement(s) and returns the number of rows modified.
|
com.google.api.core.ApiFuture<Long> |
executeUpdateAsync(Statement statement,
Options.UpdateOption... options)
Same as
#executeUpdate(Statement) , but is guaranteed to be non-blocking. |
analyzeQuery, close, executeQuery, executeQueryAsync, read, readAsync, readRow, readRowAsync, readRowUsingIndex, readRowUsingIndexAsync, readUsingIndex, readUsingIndexAsync
void buffer(Mutation mutation)
default com.google.api.core.ApiFuture<Void> bufferAsync(Mutation mutation)
buffer(Mutation)
, but is guaranteed to be non-blocking.void buffer(Iterable<Mutation> mutations)
default com.google.api.core.ApiFuture<Void> bufferAsync(Iterable<Mutation> mutations)
buffer(Iterable)
, but is guaranteed to be non-blocking.long executeUpdate(Statement statement, Options.UpdateOption... options)
IllegalArgumentException
. The effects of the DML statement will be
visible to subsequent operations in the transaction.com.google.api.core.ApiFuture<Long> executeUpdateAsync(Statement statement, Options.UpdateOption... options)
#executeUpdate(Statement)
, but is guaranteed to be non-blocking. If multiple
asynchronous update statements are submitted to the same read/write transaction, the statements
are guaranteed to be submitted to Cloud Spanner in the order that they were submitted in the
client. This does however not guarantee that an asynchronous update statement will see the
results of all previously submitted statements, as the execution of the statements can be
parallel. If you rely on the results of a previous statement, you should block until the result
of that statement is known and has been returned to the client.default ResultSetStats analyzeUpdate(Statement statement, ReadContext.QueryAnalyzeMode analyzeMode, Options.UpdateOption... options)
ReadContext.QueryAnalyzeMode.PLAN
only returns the plan for
the statement. ReadContext.QueryAnalyzeMode.PROFILE
executes
the DML statement, returns the modified row count and execution statistics, and the effects of
the DML statement will be visible to subsequent operations in the transaction.
long[] batchUpdate(Iterable<Statement> statements, Options.UpdateOption... options)
executeUpdate
in a
loop. This method returns an array of long integers, each representing the number of rows
modified by each statement.
If an individual statement fails, execution stops and a SpannerBatchUpdateException
is returned, which includes the error and the number of rows affected by the statements that
are run prior to the error.
For example, if statements contains 3 statements, and the 2nd one is not a valid DML. This
method throws a SpannerBatchUpdateException
that contains the error message from the
2nd statement, and an array of length 1 that contains the number of rows modified by the 1st
statement. The 3rd statement will not run.
com.google.api.core.ApiFuture<long[]> batchUpdateAsync(Iterable<Statement> statements, Options.UpdateOption... options)
#batchUpdate(Iterable)
, but is guaranteed to be non-blocking. If multiple
asynchronous update statements are submitted to the same read/write transaction, the statements
are guaranteed to be submitted to Cloud Spanner in the order that they were submitted in the
client. This does however not guarantee that an asynchronous update statement will see the
results of all previously submitted statements, as the execution of the statements can be
parallel. If you rely on the results of a previous statement, you should block until the result
of that statement is known and has been returned to the client.Copyright © 2022 Google LLC. All rights reserved.