【问题标题】:Read an additional document in a transaction after using one transaction.get already使用一个事务后阅读事务中的附加文档。已获取
【发布时间】:2021-12-19 12:08:38
【问题描述】:

我对 Firebase 和 NoSQL 数据库很陌生,我正在开发服务器端。

我的目标是在一个事务中读取两个集合(并锁定包含的文档以保持一致性)。一方面,我想从集合 A 中读取多达 499 个文档,另一方面,我想从集合 B 中读取一个文档,如以下代码示例所示。

export const transactionTest = function(nextFunction: () => void) {
  const collection_A_reference = admin.firestore().collection("A").limit(499);
  const collection_B_doc_B1_reference = admin.firestore().collection("B").doc("B1");

  try {
    admin.firestore().runTransaction((t) => {
      return t.get(collection_A_reference)
          .then((coll_A_snapshot) => {
            if (!(coll_A_snapshot.empty)) {
              t.get(collection_B_doc_B1_reference)
                  .then((coll_B_doc_B1_snapshot) => {
                    if (coll_B_doc_B1_snapshot.exists) {
                      let counter = coll_B_doc_B1_snapshot.get("COUNTER");
                      for (let i = 0; i < coll_A_snapshot.docs.length; i++) {
                        counter++;
                        t.update(coll_A_snapshot.docs[i].ref, {COUNTER: counter});
                      }
                      t.update(coll_B_doc_B1_snapshot.ref, {COUNTER: counter});
                    } else {
                      console.log("coll_B_doc_B1_snapshot does not exist");
                    }
                  });
            } else {
              console.log("coll_A_snapshot is empty");
            }
          });
    });
  } catch (e) {
    console.log("Transaction failure:", e);
    nextFunction();
  }
  nextFunction();
};

但是,似乎 第二个 t.get 是不允许的 并抛出以下错误:

(node:13460) UnhandledPromiseRejectionWarning: 错误: 10 ABORTED: 引用的事务已过期或不再有效

有人知道如何实现(尤其是在语法上)吗?我用谷歌搜索了很多,但我没有找到我想要的。也许我在推理如何在firebase中使用事务时也有错误。一种解决方法可能是在事务之前创建一个DocumentReferences 数组,然后使用它transaction.getAll(),但这看起来不是很优雅。

我会感谢任何帮助:)

最好的问候

【问题讨论】:

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


    【解决方案1】:

    问题不在于进行了多次Transaction#get() 调用,而在于您没有正确链接承诺。这会导致事务完成(什么都不做),并且您会收到“引用的事务已过期”错误消息。

    admin.firestore().runTransaction((t) => {
      return t.get(collection_A_reference)
        .then((coll_A_snapshot) => {
          if (!(coll_A_snapshot.empty)) {
            return t.get(collection_B_doc_B1_reference)  // <-- was missing this return
              .then((coll_B_doc_B1_snapshot) => {
                if (coll_B_doc_B1_snapshot.exists) {
                  let counter = coll_B_doc_B1_snapshot.get("COUNTER");
                  for (let i = 0; i < coll_A_snapshot.docs.length; i++) {
                    counter++;
                    t.update(coll_A_snapshot.docs[i].ref, {COUNTER: counter});
                  }
                  t.update(coll_B_doc_B1_snapshot.ref, {COUNTER: counter});
                } else {
                  console.log("coll_B_doc_B1_snapshot does not exist");
                }
              });
          } else {
            console.log("coll_A_snapshot is empty");
          }
        });
    });
    

    注意事项:

    • 事务的主体((t) =&gt; { /* ... */ } 中的任何内容)可能会被多次重新尝试。所以要小心做日志之类的事情,因为你最终可能会得到很多包含被忽略数据的日志。相反,您应该返回有关结果的信息。如果事务需要重新尝试,则该对象被丢弃并替换为下一次尝试的结果。当事务成功时,最新的对象被传回给调用者,然后基于该对象进行日志记录。事务中唯一有用的日志是跟踪尝试以及它们花费了多长时间。
    • 除非您使用带有 Promise 的 await,否则用 try/catch 块包围它是没有意义的。
    try {
      await admin.firestore().runTransaction((t) => { /* ... */ });
    } catch (e) {
      console.log("Transaction failure:", e);
      nextFunction();
    }
    

    在重构方面,您可以选择:

    const transactionResult = await admin.firestore()
      .runTransaction(async (t) => {
        const coll_B_doc_B1_snapshot = await t.get(collection_B_doc_B1_reference); // check B1 first (only reading 1 document instead of up to 499)
        if (!coll_B_doc_B1_snapshot.exists)
              return { aborted: true, type: "missing-doc" };
    
        const coll_A_snapshot = await t.get(collection_A_reference);
        if (coll_A_snapshot.empty)
          return { aborted: true, type: "empty" };
    
        // queue changes
        let counter = coll_B_doc_B1_snapshot.get("COUNTER");
        for (let i = 0; i < coll_A_snapshot.docs.length; i++) {
          counter++; // <- is this meant to increment on every loop?
          t.update(coll_A_snapshot.docs[i].ref, {COUNTER: counter});
        }
        t.update(coll_B_doc_B1_snapshot.ref, {COUNTER: counter});
    
        return { aborted: false, counter };
      })
      .catch(error => ({ aborted: true, type: "error", error }));
    
    if (transactionResult.aborted) {
      if (transactionResult.type === "empty") {
        console.error(`Update failed, because coll_A is empty`);
      } else if (transactionResult.type === "missing-doc") {
        console.error(`Update failed, because coll_B_doc_B1 is missing`);
      } else {
        console.error("Update failed, because of ${transactionResult.type}", transactionResult.error);
      }
      nextFunction();
      return;
    }
    
    console.log(`Counter was successfully updated. B1 now has a COUNTER of ${transactionResult.counter}`);
    nextFunction();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-25
      • 2020-09-27
      • 1970-01-01
      • 2018-05-11
      • 2018-10-02
      相关资源
      最近更新 更多