Firebase云函数访问Firestore为何远慢于浏览器?
Firebase云函数Firestore操作性能异常缓慢问题
我有一个小型Firebase应用,用于收集好友的简单数据并生成统计信息,包含Firebase托管的Web应用、Firestore数据库以及TypeScript编写的Cloud Functions。
所有涉及Firestore的云函数操作性能极差:简单查询耗时10-60秒,访问100份以上文档的操作甚至需要1-4分钟,该问题在可调用HTTPS函数、PubSub定时任务中均存在。
但在浏览器中执行完全相同的逻辑,原本云函数中需数分钟的操作仅需约100ms即可完成,说明逻辑和访问模式没问题,问题出在云函数的配置或底层限制上。目前这个性能问题严重限制了系统扩展能力,仅能处理十几位用户的几百KB数据。
下面是出现问题的最简函数示例:遍历约10位用户,获取他们的“Plays”集合中约500份文档,然后更新用户数据。
项目代码结构
package.json
"dependencies": { "firebase": "^9.15.0", "firebase-admin": "^10.0.2", "firebase-functions": "^4.1.1", "node-fetch": "^2.6.1", "spotify-web-api-node": "^5.0.2", "typescript": "^4.5.4", "uuid": "^9.0.0" }
index.ts
import * as functions from "firebase-functions"; import { firestore } from "firebase-admin"; import { getApps, initializeApp } from "firebase-admin/app"; if (!getApps().length) { initializeApp(); } import {recurringFunction, getUserTotals} from "./endpoints"; exports.cronFunc = functions.pubsub.schedule("0 * * * *").onRun(recurringFunction); // 还有2个类似的定时任务 exports.getUserTotals = functions.https.onRequest(async (req, res) => { res.status(200).send("Success"); getUserTotals(); } // 还有5个类似的HTTP函数
endpoints.ts
export async function getUserTotals() { const users = await (await queries.getUsers()).docs; users.forEach(async (user) => { const allItems = await queries.getAllItems(user.id); const total_duration = allItems.docs.map((item) => item.duration).reduce((a, c) => a + c, 0); queries.updateUser(user.id, { total_duration, total_items: allItems.docs.length, }); }); }
queries.ts
import {getFirestore, QuerySnapshot} from "firebase-admin/firestore"; const db = getFirestore(); function getUsers() { return db.collection("User").get() as Promise<QuerySnapshot<User>>; } async function getAllItems(userId: string) { return db.collection("User").doc(userId).collection("Items").get() as Promise<QuerySnapshot<Item>>; } // 还有20-30个其他增删改查函数
云函数执行日志
调用/getUserTotals后,日志资源管理器记录如下:
| 时间戳 | 日志内容 |
|---|---|
| 2023-01-05 00:00:00.000 MST | Function execution started |
| 2023-01-05 00:00:03.000 MST | Function execution took 3000 ms, finished with status code: 200 |
| 2023-01-05 00:00:10.000 MST | getUsers: 7.000s |
| 2023-01-05 00:00:10.000 MST | users.length 10 |
| 2023-01-05 00:02:00.000 MST | getAllItems user0 2:00:000 (m:ss.mmm) |
| 2023-01-05 00:04:00.000 MST | getAllItems user1 4:00:000 (m:ss.mmm) |
| 2023-01-05 00:06:00.000 MST | Exception from a finished function: Error: 9 FAILED_PRECONDITION: The requested snapshot version is too old. |
| 2023-01-05 00:06:00.000 MST | Exception from a finished function: Error: 9 FAILED_PRECONDITION: The requested snapshot version is too old. |
| 2023-01-05 00:06:00.000 MST | Exception from a finished function: Error: 9 FAILED_PRECONDITION: The requested snapshot version is too old. |
| 2023-01-05 00:06:00.000 MST | Exception from a finished function: Error: 9 FAILED_PRECONDITION: The requested snapshot version is too old. |
| 2023-01-05 00:06:00.000 MST | Exception from a finished function: Error: 9 FAILED_PRECONDITION: The requested snapshot version is too old. |
已尝试的优化方案
- 将函数改写为递归形式,避免了快照超时问题,但处理约4位用户后会触发15分钟超时限制
- 连续执行函数排除冷启动问题,但性能无改善
- 在浏览器中执行相同逻辑,处理测试用户的800份文档仅需约500ms,数据量约500KB
后续更新
将functions.https.onRequest改为functions.https.onCall并返回结果列表后,耗时缩短至约20秒,虽然仍比预期慢一个数量级,但至少可用,不过该优化不适用于定时任务。
我是否存在关键代码错误?还是相关库存在bug?会继续测试并更新结果。
内容的提问来源于stack exchange,提问作者Hunter Arnell
相关产品推荐
相关产品推荐

