【问题标题】:Firebase cloud function object update creating recursionFirebase 云函数对象更新创建递归
【发布时间】:2018-06-12 09:12:45
【问题描述】:

我的应用程序有一个简单的云功能来跟踪创建/更新的时间戳。它看起来像这样:

export const objectChanges = functions
  .firestore
  .document('objects/{id}')
  .onWrite(event => {
    const patch: {created_at?: string, updated_at: string} = {
      updated_at: <string> event.timestamp
    };

    if (!event.data.previous) {
      patch.created_at = event.timestamp;
    }

    return event.data.ref.set(patch, {merge: true});
  });

只要我上传这个函数并在列表中创建/修改一个对象,它就会开始不断地勾选 updated_at。我猜它正在检测它本身对 updated_at 字段所做的更改。考虑到文档显示了这种更新的示例,这种行为让我感到困惑。

https://firebase.google.com/docs/functions/firestore-events#writing_data

是否有我遗漏的细微差别,或者这是 Firestore 错误?

【问题讨论】:

  • 看起来你可能遗漏了这部分:"注意:任何时候你写入触发函数的同一个文档,你都有创建无限循环的风险。请谨慎行事,并确保在不需要更改时安全退出函数。”似乎没有任何示例证明了这一点。

标签: javascript node.js firebase google-cloud-functions google-cloud-firestore


【解决方案1】:

在 onUpdate 触发器中,将时间戳字段添加或更新到具有最新时间 (admin.firestore.fieldValue.serverTimestamp()) 的文档中。对于这个例子,我将调用时间戳变量time

onUpdate 函数为您提供beforeafter 文档。如果before.time == after.time,那么您知道用户进行了更改。如果before.time !== after.time,你就知道云功能做了改动。

if(snap.before.data().time !== snap.after.data().time){
   // The cloud function altered the document.
   // Return to prevent recursion!
   return;
}

当您从客户端更新文档时,还要将time 变量更新为最新时间。这样,云函数不会在执行所需的代码之前终止。

【讨论】:

    【解决方案2】:

    当您编写一个由自身触发的函数,但仅更改该 updated_at 字段时,您可以检查之前的 updated_at 字段是否在新的 updated_at 字段的某个范围内。

    以下代码位于您的云函数中

    .onUpdate( async (change) => {
    
    })
    
    
    
    //exit if the last write happened within 5 seconds
    if(change.after.data().updated_at - change.before.data().updated_at <= 5) {
        return;
    }
    
            const date: Date = new Date();
    
            try {
                await db
                    .collection("collection_name")
                    .doc(change.after.id)
                    .set({ updated_at: date }, { merge: true });
            } catch (e) {
                functions.logger.log("failed updating doc in collection_name: ", e);
                return false;
            }
    
    return true;
    
    

    我使用 5 秒是因为 - 我不在乎我更新的时间戳是否偏离了 5 秒 - 我不确定生产中的延迟可能有多长(这种情况也适用于 1 秒,并且模拟器中的云功能大约每 0.015 秒运行一次)

    【讨论】:

    • 恕我直言,这种模式在很多情况下都会被打破,我认为这不是其他人可以效仿的好榜样
    【解决方案3】:

    请特别注意您引用的文档示例代码部分:

    // We'll only update if the name has changed.
    // This is crucial to prevent infinite loops.
    if (data.name == previousData.name) return;
    

    您需要找到一种方法来检测您的函数执行的更新何时会在同一位置触发后续更新。

    【讨论】:

    • 同样捏 :P ...就在你之前几秒钟 :D
    【解决方案4】:

    如果您检查文档中给出的示例,则会检查一个条件以查看 name 是否已更改。如果是,则仅执行以下代码。如果它没有变化,它只是returns,像这样:

    // We'll only update if the name has changed.
    // This is crucial to prevent infinite loops.
    if (data.name == previousData.name) return;
    

    因此,在您的情况下,您也需要检查对象的实际数据(除updated_at 之外的所有字段)是否已更改。如果没有其他(除了updated_at)被改变,你应该简单地退出函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-31
      • 1970-01-01
      • 2019-02-26
      • 2020-06-21
      • 1970-01-01
      • 2013-04-29
      • 2020-04-02
      • 2019-07-14
      相关资源
      最近更新 更多