我的Firebase云功能由我的实时数据库中的路径onCreate
触发。昨天,在做了一些基本测试后,我开始在云功能日志中出现“超出配额”错误。令人不安的是,错误一直持续到来,先是每2秒钟左右,然后每8秒左右加速一次,持续约1.5小时。
这是麻烦的一小部分:
我看了一下涵盖Retrying Asynchronous Functions的文档,似乎很清楚,在大多数情况下,函数将停止执行,如果发生错误,事件将被丢弃。在我的情况下,触发事件似乎没有被清除,可能是因为我得到的错误来自我的功能之外。这是完整的错误文本:
错误:超出配额(DNS解析:每100秒);要增加配额,请在https://console.cloud.google.com/billing?project=myproject的项目中启用结算功能。功能无法执行。
是的,我意识到配额问题可以通过转移到Blaze计划轻松解决,我将很快做到这一点,但在此之前,我想了解如何处理此案例在将来。我不得不重新部署我的函数以使错误停止发生。在研究了文档后,听起来好像我也可以删除停止错误的功能,但是一旦我的应用程序投入生产,重新部署或删除功能似乎都不是一条很好的途径。
我的想法是,我必须陷入Firebase Cloud功能服务内部的重试循环中。我的日志记录都没有被击中(其中一些会在函数启动时发生,有些会在函数运行期间发生),加上消息显示“函数无法执行”,这对我来说意味着错误发生在我的代码被执行之前。由同一事件触发的另一个函数是记录相同的错误。
所以,对于知情人士,我的问题如下:
最后,还有一些注意事项可以解决一些可能有的后续问题:
是的,重复错误确实会阻止对相关函数进行任何新的调用。除了由同一事件触发的另一个函数外,其他函数仍然可以执行。
是的,我可以在一定程度上重新创建行为。如果我将每100秒的DNS解决方案配额设置为较低的值(例如5),并执行一些执行,我会得到相同的错误,每8秒左右重复一次。奇怪的是,当我以这种方式再现时,它在大约10-20次投掷后自行恢复,似乎在100秒已经过去的时间,这是有道理的。在原始事件的情况下,错误重复了一个多小时,此时我决定重新部署,停止它们。
是的,在错误开始滚动之前,我确实在日志中看到了最新的Function execution took ### ms, finished with status 'ok'
消息。重复错误后,我确实看到后续触发数据被处理了功能成功。这就是让我想知道是否是导致云功能继续尝试的触发事件的原因。
不,我的函数不会写入会重新触发的位置。
是的,我意识到我可以通过转移到Blaze计划轻松解决这个问题,以获得更高的配额。 我会这样做。首先,我想了解出现问题的机制,以便我可以做出任何改进。
答案 0 :(得分:8)
在过去的一个月里,Firebase支持(非常好,顺便说一下)来回回顾,我终于得到了一些问题的答案。我以为我会继续回答我自己的问题,希望能让其他可能遇到类似问题的人受益。
首先,我一路上学到的一个重要信息:
如果出现配额错误,则不会从事件队列中删除异步Firebase云功能的触发事件。这与删除触发事件的other types of errors不同从队列中。根据Firebase工程师的说法,这种处理配额错误的方法是设计出来的,主要是为了解决每100秒每100秒配额的情况。在这些情况下,重试可能通常是有意义的,因为成功的运行可能是< 100秒之后。
所以,回答我的具体问题......
对于功能似乎陷入循环的情况,是重新部署还是删除恢复的唯一途径?我可能采取了哪些其他可能的方法?
重新部署或删除是唯一的选择。如果函数卡在错误循环中,则重新部署该函数将清除事件队列,从而解决问题。我能够测试这种分辨率的方法,它工作得很好。如果只有一部分功能遇到配额错误,Firebase工程师也能够确认partial deploy是完全可以接受的。
这种解决方案让我觉得有点笨拙,所以希望未来对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;
});
});
<强>的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
}
输入数据
{
"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"
} ]
}
要进行测试,只需使用GCP控制台调低Firebase项目的每100秒功能调用的配额限制。将其设置为较低的值,如4.然后,导入测试数据并观察错误滚动。几次调用将成功(并不总是其中4个),然后一批重复错误将进入,直到100秒配额滚动结束,那么你将获得更多成功的结果。有趣的是,谷歌似乎没有为事件使用FIFO队列,所以如果您手动输入记录,不要指望它们必须按照输入的顺序进行处理。
所有人都说,根据付费计划(我已经升级到),配额相当高,所以我不太可能再次遇到这个问题。但是,如果我需要设计可能在未来达到配额的项目,那么了解如何处理配额错误仍然很好。
再次感谢Firebase支持部门的优秀人员提供所有帮助。