To ensure exclusive use of a Cloud Firestore document in a shared setting, is it sufficient to read, then write to a field (eg \'lockedBy\') within a transaction?
There is no native exclusive locking in Cloud Firestore. As the system is designed to run in large distributed systems (e.g, 1000's of mobile phone users or a Kubernetes cluster), it is based around optimistic locking patterns.
This means that when you do a read-write transaction, if the document you read is written to before you can commit the transaction, the transaction will fail so the client can rollback.
I'm going to assume you're coming in from a mobile SDK to start with, and will address server clients in the next section.
You can build an exclusive lock on top of this by using a separate document. For example, say you want to implement exclusive locking on documents in collection critical_data
.
For this, we're going to use a separate collection called mutex_critical_data
, with the documents inside being mutexes for documents with the same id in collection critical_data
.
Before you can access a document called doc_id
in critical_data
you'll want to perform a write transaction to set the mutex field owner
to you. I'll assume you're using Firebase Auth so you == the user's auth id auth_id
. This can be any id that uniquely identifies the user or process though.
Once you are finished with the document, delete the mutex document so others can use it.
To ensure it's exclusive and other people cannot steal it, you'll want to add some checks in your security rules definition.
// In the match section that sets the document id to 'doc_id'
function mutex_exists ()
{
return exists(/databases/$(database)/documents/mutex_critical_data/$(doc_id));
}
function mutex_owner ()
{
return get(/databases/$(database)/documents/mutex_critical_data/$(doc_id)).data;
}
function user_owns_mutex () {
return mutex_owner().owner == request.auth.uid;
}
allow write: if not(mutex_exists()) || user_owns_mutex;
You can also use rules to enforce the mutex as well, by predicating writing to the resource by also owning the mutex.
Keep in mind that security rules are for Mobile/Web SDK access and aren't used for server clients. As server clients are considered a trusted environment, rather than having the exclusivity logic enforced by rules, you'll want to do the check in the read-write transaction on the mutex.
Last point if you build this, is I'd highly recommend looking into exclusive leases rather than exclusive locks.
A lease is like a lock, but it automatically expires if the leasee doesn't (or isn't allowed) to renew the lease before a set time. This means if the client doesn't come back (e.g, client crashes), someone else will eventually be able to obtain the lease without administrator action.
The concept is the same, but rather than setting just the owner, you also set the lease time in a field. If the lease is greater than x
old, where x
is the time length of the lease, it is consider to no longer be held by the owner. Before the lease expires, you can optionally allow the owner to renew the lease by setting a new lease time.