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

Google Cloud Function定时任务无法写入Firestore问题排查

问题排查与修复方案

我帮你分析下这个问题,本地运行正常但Cloud Functions定时任务不更新Firestore,大概率是异步逻辑的处理问题,加上几个小细节没注意到,具体来看:

核心问题:函数提前终止,异步操作未完成

你用了request的回调写法,这是典型的异步回调,但你的Cloud Function是async函数,却没有等待这个回调里的所有操作完成。Cloud Functions的机制是主线程执行完毕就会终止函数,不会等回调里的异步任务(比如Firestore的写入)跑完。所以控制台显示执行成功,其实是函数提前结束了,后面的写入逻辑根本没执行完。

其他小问题

  1. 错误使用response.send():这是HTTP触发函数的用法,Pub/Sub触发的函数没有response对象,调用它会报错,虽然被catch块捕获,但也会干扰流程。
  2. 重复初始化Admin SDK:虽然你加了admin.apps.length === 0的判断,但放在函数内部每次执行都会检查,不如移到函数外面更高效(Cloud Functions会复用实例)。
  3. 错误处理不够完善:原代码的catch块只打印错误,但没有让函数标记为失败,导致你看不到实际的错误情况。

修复后的代码

我把代码改了下,解决了这些问题:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const cheerio = require('cheerio');
const cheerioTableparser = require('cheerio-tableparser');
const requestP = require('request-promise');

// 把Admin SDK初始化移到函数外部,避免重复初始化
if (admin.apps.length === 0) {
  admin.initializeApp();
}
const db = admin.firestore();

exports.scrape = functions.pubsub.schedule('0 4 * * *')
  .onRun(async (context) => {
    try {
      // 用request-promise的await版本替代回调,确保函数等待请求完成
      const html = await requestP('https://www.basketball-reference.com/leagues/NBA_2023.html#advanced-team');
      const $ = cheerio.load(html);
      cheerioTableparser($);

      const jsonData = [];
      const data = $("#advanced-team").parsetable(true, true, true);

      // 直接从i=2开始循环,跳过标题行(原代码里i=1是标题,splice可以省掉)
      for (let i = 2; i < data[0].length; i++) {
        const team = {
          "rank": data[0][i],
          "team": data[1][i],
          "wins": data[3][i],
          "losses": data[4][i],
          "oRtg": data[10][i],
          "dRtg": data[11][i],
          "pace": data[13][i]
        };
        jsonData.push(team);
        console.log(`Processing team: ${team.team}`);
      }

      // 用map生成所有Firestore写入的Promise,更简洁
      const writePromises = jsonData.map(team => 
        db.collection('nba-teams').doc(team.team).set(team)
      );

      // 等待所有写入操作完成
      await Promise.all(writePromises);
      console.log('✅ NBA数据已成功更新到Firestore');
      return null; // Pub/Sub函数返回null或Promise即可
    } catch (err) {
      console.error('❌ 抓取或更新Firestore出错:', err);
      throw err; // 抛出错误让Cloud Functions标记为失败,方便在控制台看到问题
    }
  });

额外排查步骤

如果改完代码还是不行,你可以检查这两点:

  • Cloud Function服务账号权限:本地运行用的是你本地的凭据,而Cloud Functions默认用的是[你的项目ID]@appspot.gserviceaccount.com这个服务账号。去GCP控制台的IAM页面,给这个账号加上Cloud Datastore User或者Firestore Editor的角色,确保它有写入Firestore的权限。
  • 查看完整日志:在Cloud Functions的日志页面,仔细看所有条目,包括错误日志,说不定能看到权限不足、网络请求被拦截之类的问题(不过本地正常的话,网络问题概率低,但还是要确认)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 10:22:53