【发布时间】:2020-02-19 15:49:18
【问题描述】:
简而言之:我有一个预定的 firebase 函数,它可以下载文件,将其转换为 JSON 块并将其导入实时数据库。我给了函数 2GB 内存和 540 秒(最长 9 分钟)来完成它的工作,但它仍然在大约 50% 的时间以超时结束。肯定有漏,不过好像在我的盲区。
详细说明:
我有以下预定功能,每小时运行一次。该函数调用方法updateDatabase,然后从外部服务器下载一个大小约为35MB的文件(使用方法getNewData。这个文件是一个空格分隔的(别开玩笑)文件,包含2500行和2500行数据列。我需要数据库中的每个数据点以便稍后快速读取它们。
起初我只是简单地将其转换为 json 并尝试将其导入数据库,因为 35MB 似乎没什么大不了的。然而,这使得函数每次都耗尽内存。所以我决定把它分成更小的部分。
所以当文件被下载时,我首先将它分成几行。然后我遍历行,将其拆分为列,并在参考/grid/[currentrow] 上的实时数据库上进行设置。这意味着我每次调用 set 2500 次。我尝试对每一行使用await 并使用Promise.all()(当前版本,但有时两者似乎都在某个时候挂起。我在日志中没有任何错误,只是在 540 时超时秒过去了。
预定功能:
exports.scheduledDataUpdate = functions
.region("europe-west1")
.runWith({
timeoutSeconds: 540,
memory: '2GB'
})
.pubsub.schedule("0 * * * *")
.onRun(async () => {
try {
await updateDatabase();
console.log('Database updated');
return true;
} catch(error) {
console.error(error);
return false;
}
});
调用方法updateDatabase:
async function updateDatabase() {
let data;
try {
data = await getNewData().catch(err => console.error(err));
} catch(error) {
console.error(error);
return null;
}
console.log('Data download complete');
const lines = data.split("\n"); // split the data into rows (2500)
lines.forEach((line, r) => {
const cols = line.split(/\s+/); // split the row into columns (2500)
dbUpdates.push(admin.database().ref(`/grid/${r}`).set(cols).then(() => {
if(r > 1 && r % 500 === 0) {
console.log(`updated row ${r}`); // just to get some info in logs whether some rows were processed
}
return true;
}).catch((error) => {
console.error(`Error updating row ${r} -- ${error}`);
}));
return true;
});
return await Promise.all(dbUpdates);
如果你好奇,方法getNewData:
const dataUrl = 'https://some.server/somefile'; // I guess you guess this is different in my code (it is)
async function getNewData() {
console.log('Start download of data');
return await new Promise((resolve, reject) => {
https.get(dataUrl, response => {
if (response.statusCode === 200) {
let data = "";
return response
.on("data", chunk => {
data += chunk;
})
.on("end", () => {
resolve(data);
})
.on("error", error => {
reject(error);
console.error(`error while downloading data: ${data}`);
});
} else {
switch (response.statusCode) {
case 301:
case 302:
case 303:
case 307:
console.warning(`Redirected to ${response.headers.location} [${response.statusCode}]`)
return getNewData(response.headers.location);
default:
return reject(new Error(`Could not download new data (error ${response.statusCode})`));
}
}
})
});
}
当我查看 firebase 提供的配额时(我在 Blaze 上运行,即用即付订阅),这真的不应该是一个太大的问题。显然我错过了什么或犯了一些愚蠢的错误,但对于我的生活,我看不到它。
更新 1 超时日志示例(@FrankvanPuffelen 提问)
7:15:03.224 p.m. scheduledDataUpdate Function execution started
7:15:03.546 p.m. scheduledDataUpdate Start download of data
7:15:08.035 p.m. scheduledDataUpdate Data download complete
7:22:28.390 p.m. scheduledDataUpdate updated row 500
7:24:03.244 p.m. scheduledDataUpdate Function execution took 540021 ms, finished with status: 'timeout'
【问题讨论】:
-
您能否编辑问题以包括其中一个超时调用的日志输出?您可能还想添加一些额外的日志记录,以查看在这种情况下它通过代码的路径。
-
感谢@FrankvanPuffelen 的建议,我已经更新了问题。我尝试添加更多日志记录,但在日志中看不到它。例如,我尝试在所有
sets之间等待,它只是在某个时候停止。没有错误,只是超时。此外,并不是说“更新的第 500 行”是在下载完成 7 分钟后出现的。 -
如果它在 7 分钟后出现,我立即怀疑你在某处遗漏了一个承诺,它只是在函数超时时发生。但我看不到它在哪里,所以其他人可以通过您添加的附加信息找到它。
-
我也是这么想的,我一直在乱码代码尝试不同的东西,但似乎我总是在某个地方犯同样的错误。无论如何,感谢您调查它@FrankvanPuffelen
-
我已启用数据库日志记录以查看会发生什么。我只能看到
set有时需要很长时间。有时需要一秒钟以上的时间。所以这样做 2500 次会超时。应该不会花那么长时间吧?这很奇怪,因为在回来时,这不是问题,整个运行将在 2 分钟内完成。我可以尝试将其拆分,但我不喜欢每次都重新下载,而且我无法将其存储在本地(或者我可以吗?)。
标签: node.js firebase firebase-realtime-database google-cloud-functions