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

Firebase pubsub定时函数模拟器正常 生产环境未执行Stripe调用提前退出

问题根因
  • 核心问题出在异步遍历的写法上:Array.prototype.forEach不支持等待异步回调执行完成,你在forEach里传入的async函数不会被主函数等待,主函数触发完所有遍历任务后就直接返回了。生产环境的Cloud Functions会在函数返回后立即冻结执行上下文,未执行完的Stripe调用、Firestore更新操作都会被直接中断,因此你看不到后续的执行日志。
  • 本地emulator运行正常是因为本地调试环境不会在函数返回后立即冻结进程,后台未完成的异步任务仍能继续执行,所以能跑完完整流程,这是本地和生产环境运行机制的差异导致的。
修复方案

将forEach遍历替换为支持异步等待的写法,让主函数等待所有异步操作执行完成后再返回,可选两种实现方式:

1. 串行执行(适合用户量小、避免触发Stripe接口限流)

export const reportUsage =
  fun.pubsub.schedule("0 0 1 * *").onRun(async (context) => {
    functions.logger.debug("Initialising db");
    const db = admin.firestore();
    const users = await db.collection("users").get();
    // 替换forEach为for...of 逐个等待异步操作完成
    for (const doc of users.docs) {
      functions.logger.debug("Got list of users. Looping..");
      const userData = doc.data();
      const SOME_DATA:number = userData["SOME_DATA"];
      functions.logger.debug("got SOME_DATA of this user");
      const SOME_DATAIntPart:number =
       parseInt(SOME_DATA.toFixed(20).split(".")[0]);
      const SOME_DATAFloatPart:number =
       parseFloat("0." + SOME_DATA.toFixed(20).split(".")[1]);
      const subItemId =
       userData["stripeInfo"]["subscription"]["items"]["data"][0]["id"];
      functions.logger.debug("got sub id of this user");
      await stripe.subscriptionItems.createUsageRecord(subItemId, {
        quantity: SOME_DATAIntPart,
        timestamp: admin.firestore.Timestamp.now().seconds,
      }, {
        timeout: 60,
        maxNetworkRetries: 5,
      });
      functions.logger.debug("got reported to stripe");
      await doc.ref.update({
        "SOME_DATA": SOME_DATAFloatPart,
      });
      functions.logger.debug("updated SOME_DATA");
    }
    return null;
  });

2. 并行执行(适合用户量大、提升处理效率)

export const reportUsage =
  fun.pubsub.schedule("0 0 1 * *").onRun(async (context) => {
    functions.logger.debug("Initialising db");
    const db = admin.firestore();
    const users = await db.collection("users").get();
    // 用Promise.all批量等待所有异步任务完成
    await Promise.all(users.docs.map(async (doc) => {
      functions.logger.debug("Got list of users. Looping..");
      const userData = doc.data();
      const SOME_DATA:number = userData["SOME_DATA"];
      functions.logger.debug("got SOME_DATA of this user");
      const SOME_DATAIntPart:number =
       parseInt(SOME_DATA.toFixed(20).split(".")[0]);
      const SOME_DATAFloatPart:number =
       parseFloat("0." + SOME_DATA.toFixed(20).split(".")[1]);
      const subItemId =
       userData["stripeInfo"]["subscription"]["items"]["data"][0]["id"];
      functions.logger.debug("got sub id of this user");
      await stripe.subscriptionItems.createUsageRecord(subItemId, {
        quantity: SOME_DATAIntPart,
        timestamp: admin.firestore.Timestamp.now().seconds,
      }, {
        timeout: 60,
        maxNetworkRetries: 5,
      });
      functions.logger.debug("got reported to stripe");
      await doc.ref.update({
        "SOME_DATA": SOME_DATAFloatPart,
      });
      functions.logger.debug("updated SOME_DATA");
      return null;
    }));
    return null;
  });

内容的提问来源于stack exchange,提问作者Bashar Shehab

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 18:54:08