Firebase云功能因配额错误而反复出现故障

Hon*_*Guy 10 firebase google-cloud-functions

我有一个Firebase云功能,由onCreate我的实时数据库中的路径触发.昨天,在做了一些基本测试后,我开始在云功能日志中出现"超出配额"错误.令人不安的是,错误不断发生,首先每2秒左右,然后每8秒左右加速,持续约1.5小时.

这是麻烦的一小部分:

错误. 错误. 更多错误.*叹*

我看了一下有关重试异步函数的文档,很明显,在大多数情况下,函数将停止执行,如果发生错误,事件将被丢弃.在我的情况下,触发事件似乎没有被清除,可能是因为我得到的错误来自我的功能之外.这是完整的错误文本:

错误:超出配额(DNS解析:每100秒); 要增加配额,请通过https://console.cloud.google.com/billing?project=myproject在项目中启用结算功能.功能无法执行.

是的,我意识到配额问题很容易通过转移到Blaze计划来解决,我将很快做到这一点,但在此之前,我想了解如何处理这种情况,如果将来发生的话.我不得不重新部署我的函数以使错误停止发生.在研究了文档后,听起来好像我也可以删除停止错误的功能,但是一旦我的应用程序到达生产状态,重新部署或删除功能似乎都不是一条很好的途径.

我的想法是,我必须陷入Firebase Cloud功能服务内部的重试循环中.我的日志记录都没有被击中(其中一些会在函数启动时发生,有些会在函数运行期间发生),加上消息显示"函数无法执行",这对我来说意味着错误发生在我的代码被执行之前.由同一事件触发的另一个函数是记录相同的错误.

所以,对于知情人士,我的问题如下:

  1. 对于功能似乎陷入循环的情况,是重新部署还是删除恢复的唯一途径?我可能采取了哪些其他可能的方法?
  2. 有没有办法可以处理来自我的实现之外的错误?这样的错误是由服务引发的,还是错误只能源于我的代码?我的猜测是,我仅限于监视日志中的此类故障,但也许还有其他可能的操作可以使我的服务更加健壮.

最后,还有一些注意事项可以解决一些可能有的后续问题:

  • 是的,重复错误确实阻止了对相关函数的任何新调用.除了由同一事件触发的另一个函数外,其他函数仍然可以执行.

  • 是的,我可以在一定程度上重新创建行为.如果我将每100秒的DNS解决方案配额设置为较低的值(例如5),并执行一些执行,我会得到相同的错误,每8秒左右重复一次.奇怪的是,当我以这种方式再现时,它在大约10-20次投掷后自行恢复,似乎在100秒已经过去的时间,这是有道理的.在原始事件的情况下,错误重复了一个多小时,此时我决定重新部署,停止它们.

  • 是的,Function execution took ### ms, finished with status 'ok'在错误开始滚动之前,我确实在日志中看到了最新的消息.错误完成后,我确实看到函数成功处理了最后一次触发数据.这就是让我想知道是否是导致云功能继续尝试的触发事件的原因.

  • 不,我的功能不会写入会重新触发的位置.

  • 是的,我意识到我可以通过转向Blaze计划以获得更高的配额来轻松解决这个问题.我会这样做的.首先,我想了解出错的机制,以便我可以进行任何改进.

Hon*_*Guy 11

在过去的一个月里,在Firebase支持(谁是优秀的,顺便说一句)来回回顾之后,我终于得到了一些问题的答案.我以为我会继续回答我自己的问题,希望能让其他可能遇到类似问题的人受益.

首先,我一路上学到的一个重要信息:

在配额错误的情况下,不会从事件队列中删除异步Firebase云功能的触发事件.这与从队列中删除触发事件的其他类型的错误不同.根据Firebase工程师的说法,这种处理配额错误的方法是设计的,主要是为了解决每100秒配额受到攻击的情况.在这些情况下,重试可能通常是有意义的,因为成功运行可能<100秒.

那么,回答我的具体问题......

对于功能似乎陷入循环的情况,是重新部署还是删除恢复的唯一途径?我可能采取了哪些其他可能的方法?

重新部署或删除是唯一的选择.如果函数卡在错误循环中,则重新部署该函数将清除事件队列,从而解决问题.我能够测试这种分辨率的方法,它工作得很好.如果只有一部分功能遇到配额错误,Firebase工程师也能够确认部分部署是完全可以接受的.

这种分辨率方法让我觉得有点笨拙,因此希望未来对Cloud Functions平台的改进将为开发人员提供手动清除函数事件队列的选项.手指交叉.

有没有办法可以处理来自我的实现之外的错误?这样的错误是由服务引发的,还是错误只能源于我的代码?

基本上没有.配额错误源自服务,而不是源于已部署的代码,因此无法在代码中捕获和处理它们.对于像我这样的案例,最好的选择是利用该平台出色的错误报告功能来及早发现问题并采取纠正措施.

其他一些说明......

对于想要自己查看此行为的任何人,这里是我提供给Firebase支持的MCVE的副本,可用于复制行为.

index.js

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp(functions.config().firebase);

const database = admin.database();

exports.helloWorld = functions.database.ref("/requests/" + "{pushKey}")
.onCreate(event  => {
    console.log("helloWorld: Triggered with pushKey: ", event.params.pushKey);

    //Say hello
    console.log("Hello, world! Don't panic!");

    let result = {};
    result["name"] = event.data.val().name;
    return database.ref("/responses/" + `${event.params.pushKey}/`).set(result)
    .then(() => {
        console.log("Response written to: " + `${event.params.pushKey}/`);
        return;
    })
    .catch((writeError) => {
        console.error("Response write failed at: " + `${event.params.pushKey}/`);
        return;
    });

});
Run Code Online (Sandbox Code Playgroud)

的package.json

{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "lint": "./node_modules/.bin/eslint .",
    "serve": "firebase serve --only functions",
    "shell": "firebase experimental:functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "dependencies": {
    "firebase-admin": "~5.8.1",
    "firebase-functions": "^0.8.1"
  },
  "devDependencies": {
    "eslint": "^4.12.0",
    "eslint-plugin-promise": "^3.6.0"
  },
  "private": true
}
Run Code Online (Sandbox Code Playgroud)

输入数据

{
  "requests" : [ null, {
    "name" : "Ford Prefect"
  }, {
    "name" : "Arthur Dent"
  }, {
    "name" : "Trillian"
  }, {
    "name" : "Zaphod Beeblebrox"
  }, {
    "name" : "Marvin"
  }, {
    "name" : "Slartibartfast"
  }, {
    "name" : "Deep Thought"
  }, {
    "name" : "Zarniwoop"
  }, {
    "name" : "Fenchurch"
  }, {
    "name" : "Babel Fish"
  }, {
    "name" : "Tricia McMillan"
  } ]
}
Run Code Online (Sandbox Code Playgroud)

要进行测试,只需使用GCP控制台调低Firebase项目每100秒功能调用的配额限制.将其设置为较低的值,如4.然后,导入测试数据并观察错误滚动.几次调用将成功(并不总是其中4次),然后一批重复错误将进入,直到100秒配额滚动结束,那么你将获得更多成功的结果.有趣的是,Google似乎没有为事件使用FIFO队列,因此如果您手动输入记录,不要指望它们必须按照输入的顺序进行处理.

所有人都说,根据付费计划(我已经升级到),配额相当高,所以我不太可能再次遇到这个问题.但是,如果我需要设计可能在未来达到配额的项目,那么了解配额错误的处理方式仍然很好.

再次感谢Firebase支持的优秀人员提供所有帮助.