【问题标题】:How to retry a Cloud Firestore transaction in a Cloud Function until state == true如何在 Cloud Function 中重试 Cloud Firestore 事务,直到 state == true
【发布时间】:2018-07-29 05:25:21
【问题描述】:

问题

我有一个 Cloud 函数,它根据文档 state 字段中的更改更新 Cloud Firestore 文档。这些更新必须以特定顺序发生。当快速连续进行两项更改时,无法保证 Cloud Functions 将以正确的顺序运行。有没有办法让 Cloud Firestore 事务重试直到成功或超时?

  1. Document.state 设置为 stage1
  2. Document.state 更新为 stage2
  3. Document.state 更新为 stage3
  4. 云函数被触发并读取stage3
  5. 云函数被触发并读取stage2

在 Cloud Functions 文档中,它讨论了在失败时重试事务的能力。但是,此选项在 GCP 控制台的 Cloud Functions 部分中显示为灰色(未显示在 Firebase 控制台中)

示例代码

传入的变量

myDocumentRef: db.doc('myCollection/myDocument')
newState: stage3

交易代码

var transaction = db.runTransaction(t => {
    return t.get(myDocumentRef)
        .then(doc => {
            if ((newState = 'stage2' && doc.data().state = 'stage1') ||
                (newState = 'stage3' && doc.data().state = 'stage2')) {
              t.update(myDocumentRef, { population: newPopulation });
            } else {
              // Keep retrying the transaction until it succeeds
            }
        });
}).then(result => {
    console.log('Transaction success!');
}).catch(err => {
    console.log('Transaction failure:', err);
});

【问题讨论】:

    标签: javascript firebase google-cloud-functions google-cloud-firestore


    【解决方案1】:

    默认情况下,Firestore 事务会自行重试。 The documentation for transactions 状态:

    一个事务由任意数量的 get() 操作组成,然后是 任意数量的写操作,例如 set()、update() 或 delete()。 在并发编辑的情况下,Cloud Firestore 会运行整个 再次交易。例如,如果一个事务读取文档并且 另一个客户端修改了这些文档中的任何一个,Cloud Firestore 重试事务。此功能可确保事务 运行最新且一致的数据。

    此重试采用重复调用事务处理函数(传递给 runTransaction 的函数)的形式。

    Cloud Functions 重试机制不同。它重试未完全成功的功能。有关其工作原理的详细信息,请阅读here。它与 Firestore 事务无关。这些重试的语义与使用的触发器类型无关。

    【讨论】:

    • 嘿道格。感谢您的澄清。但是,问题不在于并发运行的事务。它们没有顺序。如果不满足条件,如何让我的事务代码退出并重试?
    • Cloud Functions 不对事件的顺序提供任何保证。请记住,您的代码可以在任意数量的服务器上运行,并且让它们都遵守某些顺序会使服务器无法正常扩展。
    • 是的。我明白那个。我想知道的是,如果不满足条件,是否有办法重试事务?然后如果另一个函数写入数据,而第一个事务重试,它最终会成功(一旦满足条件)
    • 不,这不是事务的工作方式。事务处理程序永远不应该依赖可能改变的外部数据或条件。它们应该只处理静态数据和事务中正在读取的文档。
    • 所以,t.get(myDocumentRef) 读取文档,我想检查 state 字段的值。如果不是我所期望的,我希望事务失败并重复,直到它等于我所期望的。它被事务读取为state: 0。在我继续之前,我希望它是 state: 1。另一个函数也在尝试将其从 0 更改为 1。我希望其他函数在此事务将其更改为 2 之前写入 1。
    【解决方案2】:

    您的问题基本上有两种选择:

    1。使用 Google Cloud RETRY 标志

    当您部署一个函数时,您可以简单地启用重试,这将导致 google-cloud 环境在抛出 任何错误(或返回被拒绝的 Promise)时自动调用您的函数这听起来不错,直到你意识到这可能会产生!重大费用!,因为如果你的函数中有一个错误,每次都会抛出一个错误,那么该函数将被调用数千次,直到最终达到重试限制一两天后就超过了。

    您的函数可能如下所示:

    return t.get(myDocumentRef)
        .then(doc => {
            if ((newState = 'stage2' && doc.data().state = 'stage1') ||
                (newState = 'stage3' && doc.data().state = 'stage2')) {
              t.update(myDocumentRef, { population: newPopulation });
            } else {
              throw new Error('Illegal state') 
            }
        });
    }).then(result => {
        console.log('Transaction success!');
    }).catch(err => {
        if (event.timeStamp < Date.now()-300000) {
            // We have tried for 5 minutes and still an error, we give up
            console.error('Bad things happen: ', err)
        } else {
            throw new Error(err) // Firebase will receive this as a rejected Promise and retry
        }
    })
    

    2。创建自己的重试逻辑

    如果您只想在一段时间后重试一次,您可以简单地返回一个 Promise,它将通过 setTimeout() 解决,等待一小段时间然后重试。这看起来需要做更多的工作,但您可以更好地控制重试次数。但与 Firebase 重试相比,您必须处理函数的最大运行时限制。

    else {
      return new Promise(r => window.setTimeout(tryUpdate, 100))
    

    【讨论】:

      猜你喜欢
      • 2018-06-22
      • 1970-01-01
      • 2022-12-16
      • 2021-06-19
      • 2020-01-11
      • 2018-03-19
      • 2018-11-16
      • 2018-03-17
      • 1970-01-01
      相关资源
      最近更新 更多