【问题标题】:Firestore - Deleting lot of documents without concurrency issuesFirestore - 删除大量文档而没有并发问题
【发布时间】:2021-06-26 17:04:38
【问题描述】:

我使用 Firestore 开发了一款游戏,但我注意到我的预定云功能中存在一些问题,该功能会删除 5 分钟前创建但未满已完成的房间

强>。

为此,我正在运行以下代码。

async function deleteExpiredRooms() {
  // Delete all rooms that are expired and not full
  deleteExpiredSingleRooms();

  // Also, delete all rooms that are finished
  deleteFinishedRooms();
}

删除已完成的房间似乎可以正常工作:

async function deleteFinishedRooms() {
  const query = firestore
    .collection("gameRooms")
    .where("finished", "==", true);

  const querySnapshot = await query.get();

  console.log(`Deleting ${querySnapshot.size} expired rooms`);

  // Delete the matched documents
  querySnapshot.forEach((doc) => {
    doc.ref.delete();
  });
}

但我在删除 5 分钟前创建的未满房间时遇到了并发问题(当房间中有 2 个用户时,一个房间已满,因此游戏可以开始)。

async function deleteExpiredSingleRooms() {
  const currentDate = new Date();

  // Calculate the target date
  const targetDate = // ... 5 minutes ago

  const query = firestore
    .collection("gameRooms")
    .where("full", "==", false)
    .where("createdAt", "<=", targetDate);

  const querySnapshot = await query.get();

  console.log(`Deleting ${querySnapshot.size} expired rooms`);

  // Delete the matched documents
  querySnapshot.forEach((doc) => {
    doc.ref.delete();
  });
}

因为在删除房间的过程中,用户可以在房间被完全删除之前进入。

有什么想法吗?

注意:为了搜索房间,我使用的是交易

firestore.runTransaction(async (transaction) => {
  ...

  const query = firestore
    .collection("gameRooms")
    .where("full", "==", false);

  return transaction.get(query.limit(1));
});

【问题讨论】:

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


    【解决方案1】:

    你可以使用BatchWrites:

    const query = firestore
        .collection("gameRooms")
        .where("full", "==", false)
        .where("createdAt", "<=", targetDate);
    
    const querySnapshot = await query.get();
    
    console.log(`Deleting ${querySnapshot.size} expired rooms`);
    
    const batch = db.batch();
    
    querySnapshot.forEach((doc) => {
      batch.delete(doc.ref);
    });
    
    // Commit the batch
    batch.commit().then(() => {
        // ...
    });
    

    批量写入最多可包含 500 个操作。中的每个操作 该批次将单独计入您的 Cloud Firestore 使用量。

    这应该会立即删除所有符合该条件的房间。使用循环删除它们可能需要一段时间,因为它会一个一个发生。

    如果您担心批量写入中 500 个文档的限制,请考虑使用 Promise.all,如下所示:

    const deleteOps = []
    querySnapshot.forEach((doc) => {
      deleteOps.push(doc.ref.delete());
    });
    
    await Promise.all(deleteOps)
    

    现在为了防止用户加入正在被删除的房间,在云函数中这样做有点困难,因为所有实例都独立运行并且可能存在竞争条件。

    为避免这种情况,您必须手动检查用户尝试加入的房间是否超过 5 分钟并且玩家数量是否较少。这只是一个检查,以确保房间正在被删除或将被立即删除。

    function joinRoom() {
     // isOlderThanMin()
     // hasLessNumOfPlayers()
     // return 'Room suspended'
    }
    

    因为过滤哪些房间应该被删除的逻辑是相同的,这应该不是问题。

    【讨论】:

    • 这如何解决并发问题?我的意思是,我认为批量写入对于确保一次删除 N 个文档很有用,如果其中一个失败则恢复。
    • @Raul 你的意思是阻止用户在被删除时加入这些房间吗?
    • 是的,我就是这个意思
    • 由于 500 的限制,也不使用批量删除
    • @Raul 请检查更新后的答案,我更喜欢批量写入/承诺使用循环删除文档。
    【解决方案2】:

    也许您正在寻找交易,请查看此处的文档:https://firebase.google.com/docs/firestore/manage-data/transactions

    或者观看解释并发问题以及批量写入事务之间区别的YouTube视频:https://youtu.be/dOVSr0OsAoU

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-06-30
      • 1970-01-01
      • 2019-01-12
      • 1970-01-01
      • 2018-05-08
      • 2019-02-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多