【问题标题】:Firebase transaction is not repeating on failureFirebase 事务在失败时不会重复
【发布时间】:2020-08-31 17:42:21
【问题描述】:

更新:

这是我收到的console.error(err) 消息:

Error: 6 ALREADY_EXISTS: Document already exists: projects/projectOne-e3999/databases/(default)/documents/storage/AAAAAAAAAA1111111111

我明白为什么会出现错误。因为并行运行的事务更快地执行事务的else 部分。因此,返回错误的事务不必创建而是更新文档。如果事务重新运行就足够了。但它并没有声明Transactions are committed once 'updateFunction' resolves and attempted up to five times on failure

这就是问题的重点。当文档已经存在时,如何防止事务失败,因为它是并行创建的,或者如何重新运行事务?

原问题:

我有一个云功能,可以在创建另一个集合中的文档时更新另一个文档。由于可以并行创建多个文档,所有文档都访问同一个文档,这将作为事务运行,因此保证了幂等性。

函数的结构如下所示。问题是第一次写入(因此创建存储文档)。正如我所说,可以并行创建两个文档。但是现在事务读取了一个不存在的文档并尝试创建它,但是由于另一个事务(并行运行的事务)已经创建了该文档,所以事务失败了。 (首先我不明白,因为我认为事务会在执行时阻止所有访问)

这根本不是问题,但它不会重试(尽管它声明它将自动重试多达 5 次)。我认为这与我的异步函数的 try & catch 块有关。

如果它会重试,它将检测到文档存在并自动更新现有的。

让我们简单地说。此事务绝不能失败。如果没有可能的自动恢复方式,它将导致数据库不一致。

.onCreate(async (snapshot, context) => {
        //Handle idempotence
        const eventId = context.eventId;
        try {
            const process = await shouldProcess(eventId)
            if (!process) {
                return null
            }

            const storageDoc = admin.firestore().doc(`storage/${snapshot.id}`)

           
            await admin.firestore().runTransaction(async t => {
                    const storageDbDoc = await storageDoc.get()
                    const dataDb = storageDbDoc.data()

                    if (dataDb) {
                        //For sake of testing just rewrite it
                        t.update(storageDoc, dataDb )
                    } else {
                        //Create new     
                        const storage = createStorageDoc()
                        t.create(storageDoc, storage )    
                    }
            })
            return markProcessed(eventId)
        } catch (err) {
            console.error(`Execution of ${eventId} failed: ${err}`)
            throw new Error(`Transaction failed.`);
        }

【问题讨论】:

  • 请编辑问题以显示有关失败的更多详细信息,而不仅仅是“交易失败”。您应该完整地记录错误对象,而不仅仅是字符串表示。 console.error(err).
  • 基本点:事务根本不会阻塞任何东西。他们确实检查在事务处理期间读取的任何文档是否已更改。如果读取的任何文档 更改,则结果 提交并重新尝试整个事务。最关键的是:没有任何东西被锁定,没有任何东西被阻止。 firebase.google.com/docs/reference/js/…
  • @DougStevenson 我用日志更新了问题,也许现在的问题更清楚了。
  • @LeadDreamer 好的,这就解释了为什么我会收到错误消息。现在的问题是:如何防止这种情况发生。我真的只需要一个交易(有史以来第一个)创建文档。所有其他应该只更新现有文档。 (即使有多个事务并行运行)

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


【解决方案1】:

您的事务坚持能够创建新文档:

t.create(storageDoc, storage)

由于create() 在文档已经存在的情况下失败,整个事务就失败了,无法恢复。

如果您的事务必须能够恢复,您应该在尝试编写之前检查该文档是否存在,并决定在这种情况下您要做什么。

【讨论】:

  • 那么您的意思是:在尝试创建之前再次获取文档?因为我在交易一开始就已经拿到了,不是吗?为什么交易会说“失败时最多重复 5 次”?
  • 您必须使用事务对象来读取文档。您不能使用参考上的普通 get() 来做到这一点。请改用t.get(ref),以便事务知道您要锁定该文档。 googleapis.dev/nodejs/firestore/latest/Transaction.html#get
  • 天哪。我没有看到这个。耶稣。谢谢你。我会在早上尝试。我知道我必须使用事务对象,我只是没有看到我没有。如果这确实是(非常简单的)解决方案,我会回复
  • 我现在测试了很多次。看来这个问题是万恶之源。谢谢,有时必须由另一个人来说明这一点
  • @LeadDreamer 有问题请单独发帖。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-10
  • 1970-01-01
  • 1970-01-01
  • 2017-12-06
相关资源
最近更新 更多