Reputation: 1096
I have a Node.js app with @google-cloud/firestore that has two collections, A and B.
Collection A documents have a field called 'name' and one called 'collectionBId'. Collection B documents just have a field called 'name'. Name's in collection A and B are unique.
// Collection A
{ id: 1, name: 'example1', nameB: 'example2' }
// Collection B
{ id: 1, name: 'example2' }
There an endpoint in Node.js that does:
const result = await firestore.runTransaction(async transaction => {
const refA = firestore.collection('A').where('name', '==', reqParam1)
const {docs: docsA} = await transaction.get(refA)
const refB = firestore.collection('B').where('name', '==', docsA[0].data().name).where('name', '==', reqParam2)
const {docs: docsB} = await transaction.get(refB)
return docsB[0].data()
})
This is significantly faster than (there is one less round trip):
const {docs: docsA} = await firestore.collection('A').where('name', '==', reqParam1).get()
const {docs: docsB} = await firestore.collection('B').where('name', '==', docsA[0].data().name).where('name', '==', reqParam2).get()
const result = docsB[0].data()
The transactions run in pessimistic locks in Node.js but is not clear if multiple users can use this transaction concurrently (eg. 100 users).
On the docs, it is mentioned that when a write is performed there is a lock in place that prevent a concurrent write will that prevent reads also? Also, if a read transaction is used a lot, would that block writes?
That leads to the following two questions:
Upvotes: 4
Views: 1068
Reputation: 1494
(Final?) Edit 4: The github issue was marked fixed and documentation will be updated so now we know how transactions work:
You can use the transaction object passed to
updateFunction
to read and modify Firestore documents under lock. You have to perform all reads before before you perform any write.
Documents read during a transaction are locked pessimistically. A transaction lock on a document blocks other transactions, batched writes, and other non-transactional writes from changing that document. A transaction releases its document locks at commit time or once it times out or fails for any reason.
Transactions are committed once
updateFunction
resolves. If a transaction fails with contention, the transaction is retried up to five times. TheupdateFunction
is invoked once for each attempt.
Transactions timeout after 60 seconds if no documents are read. Transactions that are not committed within than 270 seconds are also aborted.
Edit 3a: I found more "official" information regarding the transactions from the Github repo:
If you are reading a document using the runTransaction() API via a Server SDK the backend will prevent other clients from modifying the document until the transaction is committed. This lock can be held for up to 270 seconds. We refer to this as "pessimistic locking" in our documentation and all Firestore Server SDK expose these semantics.
There are some rare cases when the backend aborts currently running transactions (e.g. if there is too much contention), in which case all SDKs will retry, independent of how locks were obtained.
If you have two transactions running that both try to read the same document, the first one will succeed and the second one will hang until the first one completes.
Also raised a github issue asking to improve the documentation regarding Transactions.
Edit 2: For server side SDK I found a response that might help, however there's no official documentation regarding how the transactions work besides that it runs pessimistic locks which in theory allow all reads unless it tries to write in which case it will place an exclusive lock on the record to prevent other users from manipulating it
Edit: The next responses only apply to Client side SDK which runs optimistic locks, not to server side SDK which runs pessimistic locks
In fact you have to be aware that a reading transaction could run more than once if the content read is changed while the transaction is being performed, this is done to make sure the content is the most recent.
More about this behaviour can be found here
if a transaction reads documents and another client modifies any of those documents, Cloud Firestore retries the transaction. This feature ensures that the transaction runs on up-to-date and consistent data.
And here
(When using transactions, note that) A function calling a transaction (transaction function) might run more than once if a concurrent edit affects a document that the transaction reads.
Upvotes: 3