【问题标题】:Do Firestore transaction locks block reads? (Server side SDK)Firestore 事务锁会阻止读取吗? (服务器端SDK)
【发布时间】:2020-06-08 12:49:29
【问题描述】:

我有一个带有 @google-cloud/firestore 的 Node.js 应用,它有两个集合,A 和 B。

Collection 一个文档有一个名为“name”的字段和一个名为“collectionBId”的字段。集合 B 文档只有一个名为“名称”的字段。集合 A 和 B 中的名称是唯一的。

// Collection A
{ id: 1, name: 'example1', nameB: 'example2' }

// Collection B
{ id: 1, name: 'example2' }

Node.js 中有一个端点:

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()
})

这明显快于(少了一次往返):

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()

事务在 Node.js 中以悲观锁的形式运行,但不清楚多个用户是否可以同时使用此事务(例如 100 个用户)。

在文档中,提到当执行写入时,有一个锁可以防止并发写入,是否也会阻止读取?另外,如果一个读事务被大量使用,那会阻塞写吗?

这就引出了以下两个问题:

  1. Firestore 读取事务是否会阻止 Node.js 中的其他读取事务?
  2. Firestore 读取事务会阻止 Node.js 中的写入事务吗?

【问题讨论】:

  • 你能检查我的答案吗?有一些信息可以让你清楚交易是如何运作的
  • 文档将使用 github 问题进行更新,现在我们知道交易是如何工作的@AndreasTzionis

标签: node.js firebase google-cloud-firestore transactions


【解决方案1】:

(最终?)编辑 4: github issue 被标记为已修复,文档将被更新,所以现在我们知道事务work 是如何进行的:

您可以使用传递给updateFunction 的事务对象来读取和修改处于锁定状态的 Firestore 文档。在执行任何写入之前,您必须执行所有读取。

在事务期间读取的文档被悲观地锁定。一种 文档上的事务锁定阻止其他事务,批处理 写入,以及来自更改该文档的其他非事务性写入。 事务在提交时或一次释放其文档锁 出于任何原因退出或失败。

事务在updateFunction 解决后提交。如果事务因争用而失败,则事务最多重试五次。每次尝试都会调用一次updateFunction

如果没有读取任何文档,事务会在 60 秒后超时。未在 270 秒内提交的事务也会中止。

编辑 3a: 我从 Github repo 找到了更多关于交易的“官方”信息:

如果您通过服务器 SDK 使用 runTransaction() API 读取文档,则后端将阻止其他客户端修改文档,直到事务提交。此锁定最多可保持 270 秒。我们在文档中将此称为“悲观锁定”,所有 Firestore Server SDK 都公开了这些语义。

后端中止当前正在运行的情况很少见 交易(例如,如果有太多的争用),在这种情况下,所有 SDK 将重试,与获取锁的方式无关。

Also

如果您有两个正在运行的事务都尝试读取相同的内容 文档,第一个成功,第二个挂起 直到第一个完成。

还提出了一个 github issue 要求改进有关交易的文档。

编辑 2: 对于服务器端 SDK,我发现了一个 response 可能会有所帮助,但是没有关于事务如何工作的官方文档,除了它运行悲观锁,理论上允许所有读取,除非它会尝试写入,在这种情况下,它将在记录上放置一个排他锁以防止其他用户对其进行操作

编辑:接下来的回复仅适用于运行乐观锁的客户端 SDK,不适用于运行悲观锁的服务器端 SDK

  1. 不,一个读取事务不会阻塞另一个读取事务。
  2. 不,读事务不会阻塞写事务。

实际上,您必须注意,如果在执行事务时更改了读取的内容,则读取事务可能会运行多次,这样做是为了确保内容是最新的。

可以在here找到更多关于这种行为的信息

如果一个事务读取了文档,而另一个客户端修改了这些文档中的任何一个,Cloud Firestore 会重试该事务。此功能可确保事务在最新且一致的数据上运行。

这里

(使用事务时,请注意)如果并发编辑影响事务读取的文档,则调用事务(事务函数)的函数可能会运行多次。

【讨论】:

  • 请注意,您在答案中包含的 Firebase 文档摘录对使用乐观并发进行交易的客户端 SDK 有效,但对 Node.js SDK(即由 OP 使用)。对于从后端执行的事务(例如,使用 Node.js SDK)机制与客户端 SDK 不同:它们实现悲观并发(在事务期间锁定文档)。
  • @RenaudTarnec 虽然服务器 SDK 使用悲观并发,但行为几乎与客户端 SDK 相同:争用时,事务函数重试。使用服务器 SDK 与使用客户端 SDK 的开发人员的有效区别是什么?
猜你喜欢
  • 2019-10-02
  • 1970-01-01
  • 1970-01-01
  • 2019-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-31
  • 2012-05-04
相关资源
最近更新 更多