Cloud Firestore 文档锁定
Posted
技术标签:
【中文标题】Cloud Firestore 文档锁定【英文标题】:Cloud Firestore document locking 【发布时间】:2018-04-12 02:01:52 【问题描述】:为确保在共享设置中独占使用 Cloud Firestore 文档,在事务中读取然后写入字段(例如“lockedBy”)是否足够?
【问题讨论】:
【参考方案1】:悲观与乐观
Cloud Firestore 中没有本机独占锁定。由于该系统旨在运行在大型分布式系统(例如,成千上万的手机用户或 Kubernetes 集群)中,因此它基于乐观锁定模式。
这意味着当你做一个读写事务时,如果你读到的文档在你提交事务之前被写入,事务就会失败,客户端可以回滚。
构建您自己的 - 移动 SDK 和安全规则
我将假设您从移动 SDK 开始,并将在下一节中介绍服务器客户端。
您可以使用单独的文档在此之上构建排他锁。例如,假设您要对集合 critical_data
中的文档实现排他锁定。
为此,我们将使用一个名为mutex_critical_data
的单独集合,其中的文档是集合critical_data
中具有相同ID 的文档的互斥体。
在您可以访问critical_data
中名为doc_id
的文档之前,您需要执行写入事务来为您设置互斥字段owner
。我假设您使用的是 Firebase 身份验证,因此您 == 用户的身份验证 ID auth_id
。不过,这可以是唯一标识用户或进程的任何 id。
完成文档后,删除 mutex 文档以便其他人使用。
为确保它是专有的并且其他人无法窃取它,您需要在安全规则定义中添加一些检查。
// 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;
您还可以使用规则来强制执行互斥锁,方法是通过同时拥有互斥锁来预测写入资源。
构建您自己的 - 服务器客户端
请注意,安全规则适用于移动/网络 SDK 访问,而不用于服务器客户端。由于服务器客户端被认为是受信任的环境,而不是由规则强制执行排他性逻辑,您需要在互斥体上的读写事务中进行检查。
租赁而不是锁定
如果您构建它,最后一点,我强烈建议您查看独占租约而不是独占锁。
租约就像一把锁,但如果承租人不(或不允许)在设定的时间之前续订租约,它会自动到期。这意味着如果客户端不回来(例如,客户端崩溃),其他人最终将能够在没有管理员操作的情况下获得租约。
概念是相同的,但您不仅可以设置所有者,还可以在字段中设置租用时间。如果租约大于x
old,其中x
是租约的时间长度,则认为不再由所有者持有。在租约到期之前,您可以选择允许所有者通过设置新的租约时间来续订租约。
【讨论】:
感谢您解释如何实现悲观锁定。我可以理解这是关键应用程序所必需的。如果我们忽略恶意用户,并假设所有用户首先使用同一个事务进行检查,那么事务中的 read-check-for-locked-field-write 是否就足够了?如果还不够,那么我需要阅读更多关于 FireStore 事务中“原子性”的含义。 是的,假设信任就足够了 嘿@DanMcGrath,你的回答总是很棒!感谢您的努力。 谢谢@VictorNascimento!很高兴您发现它们对您有所帮助:D【参考方案2】:如果Firestore transactions 做你想做的事,你可能根本不需要实现锁。我正在考虑锁定一个文档,以便在更新之前首先检查该文档中的一个字段,但 Firestore 事务允许我锁定该文档,这样我就可以在没有任何其他进程访问该文档的情况下读取和更新文档。
【讨论】:
【参考方案3】:如果您使用 Admin SDK(或相应的 REST API),全局锁定的一种选择是使用 create 方法。
...如果文档存在于其位置,这将导致写入失败。
【讨论】:
以上是关于Cloud Firestore 文档锁定的主要内容,如果未能解决你的问题,请参考以下文章
Cloud Firestore 文档添加给出错误“参数“数据”的值不是有效的 Firestore 文档。不能使用“未定义”作为 Firestore 值”
Cloud Functions:如何将 Firestore 集合复制到新文档?
即使文档中缺少请求的字段,如何安全地从 cloud-firestore 获取数据?
如何在 Cloud 函数中读取 Firestore 文档更新之前/之后的值?