【问题标题】:Error: deadline-exceeded - when calling a firebase callable cloud function (onCall). onRequest works fine错误:已超过期限 - 调用 firebase 可调用云函数 (onCall) 时。 onRequest 工作正常
【发布时间】:2019-10-10 00:04:58
【问题描述】:

在调用 firebase 可调用云函数 (onCall) 时,我在前端收到“超出期限”错误。

我知道我必须返回一个 Promise 以便函数知道何时清理自己,但它仍然无法正常工作。

60 秒后,“deadline-exceeded”被抛出到前端,但该函数继续在服务器上运行并成功完成。所有批处理操作都写入 Firestore。

10:37:14.782 AM
syncExchangeOperations
Function execution took 319445 ms, finished with status code: 200
10:36:57.323 AM
syncExchangeOperations
Function execution started
10:36:57.124 AM
syncExchangeOperations
Function execution took 170 ms, finished with status code: 204
10:36:56.955 AM
syncExchangeOperations
Function execution started
async function syncBinanceOperations(
  userId,
  userExchange,
  userExchangeLastOperations,
  systemExchange
) {
  try {
    const client = Binance({
      apiKey: userExchange.apiKey,
      apiSecret: userExchange.privateKey
    });

    const batch = admin.firestore().batch();
    const lastOperations = userExchangeLastOperations
      ? userExchangeLastOperations
      : false;

    const promises = [];

    promises.push(
      syncBinanceTrades(client, lastOperations, userId, systemExchange, batch)
    );
    promises.push(
      syncBinanceDeposits(client, lastOperations, userId, systemExchange, batch)
    );
    promises.push(
      syncBinanceWhitdraws(
        client,
        lastOperations,
        userId,
        systemExchange,
        batch
      )
    );
    promises.push(
      updateUserExchange(userId, userExchange.id, {
        lastSync: moment().format('x')
      })
    );

    await Promise.all(promises);
    return batch.commit();
  } catch (error) {
    return handleErrors(error);
  }
}

exports.syncExchangeOperations = functions.https.onCall(
  async (data, context) => {
    try {
      userAuthenthication(data.userId, context.auth);
      let user = await getUser(data.userId);

      if (!user.plan.benefits.syncExchanges) {
        throw 'Operação não autorizada para o plano contratado';
      }

      let userExchange = await getUserExchange(data.userId, data.exchangeId);

      let response = await Promise.all([
        getUserLastOperations(data.userId, userExchange.exchangeId),
        getSystemExchange(userExchange.exchangeId)
      ]);

      let userExchangeLastOperations = response[0];
      let systemExchange = response[1];

      switch (systemExchange.id) {
        case 'binance':
          return syncBinanceOperations(
            user.id,
            userExchange,
            userExchangeLastOperations,
            systemExchange
          );
      }
    } catch (error) {
      return handleErrors(error);
    }
  }
);

如果我将此函数更改为 HTTP 请求,它会正常工作。它等待函数完成并返回。

exports.syncExchangeOperations = functions
  .runWith(runtimeOpts)
  .https.onRequest((req, res) => {
    return cors(req, res, async () => {
      try {
        let auth = await admin.auth().verifyIdToken(req.get('Authorization').split('Bearer ')[1]);

        let userExchange = await getUserExchange(
          auth.uid,
          req.query.exchangeId
        );

        let response = await Promise.all([
          getUserLastOperations(auth.uid, userExchange.exchangeId),
          getSystemExchange(userExchange.exchangeId)
        ]);

        let userExchangeLastOperations = response[0];
        let systemExchange = response[1];

        switch (systemExchange.id) {
          case 'binance':
            await syncBinanceOperations(
              auth.uid,
              userExchange,
              userExchangeLastOperations,
              systemExchange
            );
        }
        res.status(200).send();
      } catch (error) {
        res.status(401).send(handleErrors(error));
      }
    });
  });

【问题讨论】:

    标签: javascript firebase google-cloud-functions


    【解决方案1】:

    您遇到的“deadline-exeeded”是客户端上的 Firebase Javascript 库(不是函数本身)抛出的错误。 Firebase 文档缺少关于如何在可调用函数上使用 functions.runWithOptions() 的文档。出于某种原因,functions().httpsCallable() 在客户端有一个内置超时。

    因此,如果您在 Node.js 函数上使用它:

    exports.testFunction = functions.runWith({ timeoutSeconds: 180 }).https.onCall(async (data, ctx) => {
    // Your Function Code that takes more than 60second to run
    });
    

    您需要在客户端上覆盖 Javascript 库 超时设置,如下所示:

    let testFunction = firebase.functions().httpsCallable("testFunction", {timeout: 180000});
    

    我不知道客户端内置超时的目的是什么,对我来说它没有任何目的,因为它甚至不会停止服务器上函数的执行。但它一定是出于某些内部原因。

    注意 Node.js timeoutSecondsseconds 为单位,客户端库上的 timeout 选项以毫秒

    【讨论】:

    • 一个传奇的答案谢谢 - 这将节省我很多时间
    【解决方案2】:

    “Deadline exceeded”是指从客户端的角度来看,函数调用超时。默认值为 60 秒。

    尝试增加客户端和函数的超时,以便在达到客户端超时之前有时间完成。您可以通过在 HttpsCallableOptions 对象中指定它来做到这一点。

    还可以尝试返回 batch.commit() 以外的其他内容。无论该函数返回什么,都将被序列化并发送到客户端,这可能会导致问题。相反,只需 await batch.commit() 然后返回一些可预测的东西,比如一个普通的 JavaScript 对象。

    有关设置超时的信息,请参阅 API 文档:

    【讨论】:

    • 就是这样!!谢谢!
    • 或者用你的普通 JS 对象(而不是 await)排队另一个 then
    • 你怎么知道默认是 60 秒。我找不到这个信息
    猜你喜欢
    • 2022-09-26
    • 2018-12-06
    • 1970-01-01
    • 2020-07-07
    • 2021-09-08
    • 2019-11-30
    • 2021-08-29
    • 2021-02-21
    • 2022-01-23
    相关资源
    最近更新 更多