You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase Cloud Function文档变更并发触发致重复预订问题求助

解决Firebase云函数并发触发导致候补重复填补的问题

核心问题根源

你当前的事务可能只覆盖了预订更新环节,没有将候补名单的查询+标记/移除纳入同一事务,导致并发触发的两个事务同时读到同一个未处理的候补,进而重复创建预订。

解决方案

必须确保「查询候补→标记候补为已处理→更新预订」的完整流程都在Firebase事务内执行,利用事务的原子性和自动重试机制避免并发冲突。以下是具体实现方案:

1. 给候补文档增加状态字段

在候补集合的每个文档中添加status字段,可选值:

  • pending:待处理(初始状态)
  • processed:已被纳入预订
  • cancelled:用户主动退出候补

2. 完整事务逻辑实现

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

exports.fillVacancyOnCancel = functions.firestore
  .document("bookings/{bookingId}")
  .onUpdate(async (change, context) => {
    const preBooking = change.before.data();
    const postBooking = change.after.data();

    // 仅处理「从已确认变为取消」的预订变更
    if (preBooking.status === "confirmed" && postBooking.status === "cancelled") {
      const db = admin.firestore();
      const waitlistCol = db.collection("waitlist");

      return db.runTransaction(async (tx) => {
        // 步骤1:事务内查询第一个待处理的候补(按加入时间排序)
        const waitlistQuery = waitlistCol
          .where("status", "==", "pending")
          .orderBy("createdAt", "asc")
          .limit(1);
        const waitlistSnap = await tx.get(waitlistQuery);

        if (waitlistSnap.empty) return null; // 无候补可填补

        const waitlistDoc = waitlistSnap.docs[0];
        const waitlistUser = waitlistDoc.data();

        // 步骤2:事务内标记候补为已处理(原子操作,其他事务无法再读取到该候补)
        tx.update(waitlistDoc.ref, { status: "processed" });

        // 步骤3:事务内更新原取消的预订为候补用户的确认预订
        tx.update(change.after.ref, {
          userId: waitlistUser.userId,
          status: "confirmed",
          filledFromWaitlist: true,
          updatedAt: admin.firestore.FieldValue.serverTimestamp(),
        });

        // 可选:若不需要保留候补历史,可在此删除候补文档
        // tx.delete(waitlistDoc.ref);

        return `已用候补用户${waitlistUser.userId}填补空位`;
      }).catch((err) => {
        functions.logger.error("事务执行失败:", err);
        throw err;
      });
    }

    return null;
  });

3. 关键优化点说明

  • 全流程原子化:所有涉及候补和预订的读写操作都在事务内完成,Firebase会自动处理并发锁,确保同一时刻只有一个事务能修改目标候补文档。
  • 状态标记防重复:候补被标记为processed后,其他并发事务的查询会跳过该文档,避免重复选取。
  • 事务自动重试:当事务因并发冲突失败时,Firebase会自动重试,重试时会读取最新的候补状态,不会重复处理同一用户。

额外注意事项

  • 候补集合的status和createdAt字段需要创建复合索引,确保查询高效执行。
  • 事务内操作尽量精简,避免长时间运行导致超时(Firebase事务默认超时时间为60秒)。
  • 可在预订文档中增加filledFromWaitlist等标记字段,便于后续排查数据问题。

内容的提问来源于stack exchange,提问作者Adham Saheb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 20:02:33