簡體   English   中英

在 Google Cloud Firestore 的子集合中更新依賴於多個文檔的字段時,如何避免競爭條件?

[英]How can I avoid a race condition when updating a field that's dependent multiple documents in a subcollection in Google cloud firestore?

這是我的 Firestore collections 數據模型的簡化表示

// /posts/{postId}
interface Post {
  id: string; 
  lastCommentedAt: Timestamp | null
}

// /posts/{postId}/comments/{commentId}
interface Comment {
  id: string;
  createdAt: Timestamp;
}

所以有一個帖子集合,每個帖子中都有評論的子集合。

如果帖子沒有評論,則lastCommentedAt字段應為 null。否則, lastCommmentedAt應與該帖子的最新評論的createdAt值匹配。 該字段的用途是使我能夠查詢帖子並根據最近評論的帖子對它們進行排序。

我在考慮刪除評論的場景時遇到了麻煩。

刪除評論時,如果被刪除的評論是該帖子的最新評論,則lastCommentedAt的值將變得陳舊,因此需要更新。 這是我努力想出一個安全解決方案的地方。

我的第一個想法是查詢與要刪除的評論不匹配的帖子的最新評論,然后進行批量寫入,刪除評論並更新帖子的lastCommentedAt 此處使用 Firebase web SDK 的示例 Javscript 代碼:

async function deleteComment(post, comment) {
  const batch = writeBatch(firestore);
  const postRef = doc("posts", post.id);
  const commentRef = doc("posts", post.id, "comments", comment.id);
  const commentsCollection = collection("posts", post.id, "comments");

  const recentCommentSnapshot = await getDocs(
    query(
      commentsCollection,
      where("id", "!=", comment.id),
      orderBy("createdAt", "desc"),
      limit(1)
    )
  );

  let lastCommentedAt = null;
  if (recentCommentSnapshot.docs.length > 0) {
    lastCommentedAt = recentCommentSnapshot.docs[0].data().createdAt;
  }

  batch.delete(commentRef);
  batch.update(postRef, { lastCommentedAt });
  await batch.commit();
}

但是,我相信如果在查詢之后但在寫入之前創建了新評論,或者如果在查詢之后但在寫入之前刪除了最近的評論,那么上面的代碼將容易受到競爭條件的影響。

所以我想我需要一個交易,但你不能在交易中查詢文件,所以我不太確定從這里到 go 的哪里。

您可以使用Firestore Getting real-time updates來監聽快照之間的變化。 這是文檔中的示例:

import { collection, query, where, onSnapshot } from "firebase/firestore";

const q = query(collection(db, "cities"), where("state", "==", "CA"));
const unsubscribe = onSnapshot(q, (snapshot) => {
  snapshot.docChanges().forEach((change) => {
    if (change.type === "added") {
        console.log("New city: ", change.doc.data());
    }
    if (change.type === "modified") {
        console.log("Modified city: ", change.doc.data());
    }
    if (change.type === "removed") {
        console.log("Removed city: ", change.doc.data());
    }
  });
});

在這里,您可以收聽對文檔的任何更改。


檢查我創建的代碼,該代碼在從 firestore 中刪除文檔時觸發並自動更新posts.lastCommentedAt

    var latestCreatedAt = null;
    const q = query(
        collection(db, "posts", "post.id", "comments")
    );
    const recentCommentQuery = query(
        collection(db, "posts", "post.id", "comments"),
        orderBy("createdAt", "desc"),
        limit(1)
    );
    const postsRef = query(collection(db, "posts"));
    const postRef = doc(db, "posts", "post.id"); 

    async function deletedComment (createdAt) {
        const querySnapshot = await getDocs(recentCommentQuery);

        querySnapshot.forEach((doc) => {
            latestCreatedAt = doc.data().createdAt.toDate();
        });

        await updateDoc(postRef, {
            lastCommentedAt: latestCreatedAt
        })
    }

    const unsubscribe = onSnapshot(q, (snapshot, querySnapshot) => {
        snapshot.docChanges().forEach((change) => {
            if (change.type === "removed") {
                deletedComment(change.doc.data().createdAt.toDate());
            }
        });
    });

您甚至可以在添加或修改也會觸發更新posts.lastCommentedAt的文檔時添加條件

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM