This page describes transactional data contention, serializability, and
isolation. For transaction code samples, see
transactions and batched writes
instead.
Transactions and data contention
For a transaction to succeed, the documents retrieved by its read operations
must remain unmodified by operations outside the transaction. If another
operation attempts to change one of those documents, that operations enters
a state of data contention with the transaction.
- Data contention
- When two or more operations compete to control the same document. For example,
one transaction might require a document to remain consistent while a concurrent
operation tries to update that document's field values.
Cloud Firestore resolves data contention by delaying or failing one of
the operations. The Cloud Firestore client libraries
automatically retry transactions that fail due to data contention. After a
finite number of retries, the transaction operation fails and returns an error
message:
ABORTED: Too much contention on these documents. Please try again.
When deciding which operation to fail or delay, behavior depends the type of
client library.
Data contention in the mobile/web SDKs
The mobile/web SDKs (Apple platforms, Android, Web, C++) use optimistic concurrency controls to
resolve data contention.
- Optimistic concurrency controls
- Based on the assumption that data contention is not likely or that it's not
efficient to hold database locks. Optimistic transactions do not use database
locks to block other other operations from changing data.
Mobile/web SDKs use optimistic concurrency controls, because they can operate in
environments with high latency and an unreliable network connection. Locking
documents in a high latency environment would cause too many data contention
failures.
In the Mobile/Web SDKs, a transaction keeps track of all the documents you read
inside the transaction. The transaction completes its write operations
only
if
none of those documents changed during the transaction's execution. If any
document did change, the transaction handler retries the transaction. If
the transaction can't get a clean result after a few retries, the transaction
fails due to data contention.
Data contention in the server client libraries
The server client libraries (C#, Go, Java, Node.js, PHP, Python, Ruby) use
pessimistic concurrency controls resolve data contention.
- Pessimistic concurrency controls
- Based on the assumption that data contention is likely. Pessimistic
transactions use database locks to prevent other operations from modifying data.
Server client libraries use pessimistic concurrency controls, because they
assume low latency and a reliable connection to the database.
In the server client libraries, transactions place locks on the documents they
read. A transaction's lock on a document blocks other transactions,
batched writes, and non-transactional writes from changing that document. A
transaction releases its document locks at commit time. It also
releases its locks if it times out or fails for any reason.
When a transaction locks a document, other write operations must wait for the
transaction to release its lock. Transactions acquire their locks in
chronological order.
Serializable isolation
Data contention between transactions is closely related to database isolation
levels. A database's
isolation level
describes how well the system
handles conflicts between concurrent operations. Conflict comes from the
following database requirements:
- Transactions require accurate, consistent data.
- To efficiently use resources, databases execute operations concurrently.
In systems with a low isolation level, a read operation within a transaction
might read inaccurate data from uncommitted changes in a concurrent
operation.
Serializable isolation
defines the highest isolation level. Serializable
isolation means that:
- You can assume that the database executes transactions in series.
- Transactions are not affected by uncommitted changes in concurrent operations.
This guarantee must hold even while the database executes multiple
transactions in parallel. The database must implement concurrency controls to
resolve conflicts that would break this guarantee.
Cloud Firestore guarantees serializable isolation of transactions.
Transactions in Cloud Firestore are serialized and isolated by commit
time.
Serializable isolation by commit time
Cloud Firestore assigns each transaction a commit time which represents
a single point in time. When Cloud Firestore commits a transaction's
changes to the database, you can assume all reads and writes within the
transaction take place exactly at the commit time.
Actual execution of a transaction requires some span of time. The execution of a
transaction begins before the commit time, and the execution of multiple
operations may overlap. Cloud Firestore upholds serializable isolation
and guarantees that:
- Cloud Firestore commits transactions in order by commit time.
- Cloud Firestore isolates transactions from concurrent
operations with a later commit time.
In the case of data contention between concurrent operations,
Cloud Firestore uses optimistic and pessimistic concurrency controls to resolve contention.
Isolation within a transaction
Transaction isolation also applies to write operations within a transaction.
Queries and reads inside a transaction do not see the results of previous writes
inside that transaction. Even if you modify or delete a document within a
transaction, all document reads in that transaction return the version of the
document at commit time, before the transaction's write operations.
Read operations return nothing if the document did not exist then.
Issues with data contention
For more information on data contention and how to resolve them check out the
troubleshooting page
.