英文:
Firestore cloud function to manipulate collection
问题
I have two collections:
Db.collection(groups)
Db.collection(users)
The group doc stores a map of members.
Id like to fire a function which iterates over the collection, storing aggregated data from the user document (eg. Number activities last week)
How to handle this, if the collection contains many documents?
我有两个集合:
Db.collection(groups)
Db.collection(users)
群组文档存储了成员的映射。
我想触发一个函数,遍历集合,从用户文档中存储聚合数据(例如,上周的活动数量)。
如果集合包含许多文档,如何处理?
I would solve this with a promise. But i guess i run into limits of collection.get() or a timeout during the forEach loop.
Is a paginated read (.startAt()) the solution here? What about the timeout?
我会使用一个Promise来解决这个问题。但我猜想在forEach循环中会遇到collection.get()的限制或超时问题。
在这里,分页读取(.startAt())是解决方案吗?超时问题怎么解决?
export const someMethod = functions.https.onRequest((req, res) => {
let stuff: any[] = [];
let db = getFirestore();
db.collection("groups").get().then(snapshot => {
snapshot.forEach(doc => {
var newelement = {
"Members.${doc.id}.activities": doc.data().activities.length }
//batched write for every doc
// ...
});
res.send("ok")
return "";
}).catch(reason => {
res.send(reason)
})
});
export const someMethod = functions.https.onRequest((req, res) => {
let stuff: any[] = [];
let db = getFirestore();
db.collection("groups").get().then(snapshot => {
snapshot.forEach(doc => {
var newelement = {
"Members.${doc.id}.activities": doc.data().activities.length }
//批量写入每个文档
// ...
});
res.send("ok")
return "";
}).catch(reason => {
res.send(reason)
})
});
英文:
I have two collections:
Db.collection(groups)
Db.collection(users)
The group doc stores a map of members.
Id like to fire a function which iterates over the collection, storing aggregated data from the user document (eg. Number activities last week)
How to handle this, if the collection contains many documents?
I would solve this with a promise. But i guess i run into limits of collection.get() or a timeout during the forEach loop.
Is a paginated read (.startAt()) the solution here? What about the timeout?
export const someMethod = functions.https.onRequest((req, res) => {
let stuff: any[] = [];
let db = getFirestore();
db.collection("groups").get().then(snapshot => {
snapshot.forEach(doc => {
var newelement = {
"Members.${doc.id}.activities": doc.data().activities.lenth }
//batched write for every doc
// ...
});
res.send("ok")
return "";
}).catch(reason => {
res.send(reason)
})
});
答案1
得分: 0
在 Cloud Functions v1 中,单个函数调用最长可运行 9 分钟,而在 gen 2 中最长可运行 60 分钟。如果对于你的工作负载来说不够,你将不得不将其拆分成多个调用 - 例如,通过让你的主函数创建多个任务。
了解更多信息,请参考:
- https://stackoverflow.com/questions/71907617/gcp-cloud-function-9-minutes-quota-what-other-options-do-i-have
- https://stackoverflow.com/questions/57078269/do-google-cloud-background-functions-have-max-timeout/72350367#72350367
- 绕过 Google Cloud Functions 9 分钟限制的方法 ↗
- https://stackoverflow.com/questions/52883623/is-there-a-way-that-cloud-function-can-deal-with-long-run-operation
英文:
On Cloud Functions v1 a single function invocation can run up to 9 minutes, on gen 2 it can run up to 60 minutes. If that isn't enough for your workload, you will have to split it out over multiple invocations - for example by having your main function create a number of tasks.
For more on this, see:
- https://stackoverflow.com/questions/71907617/gcp-cloud-function-9-minutes-quota-what-other-options-do-i-have
- https://stackoverflow.com/questions/57078269/do-google-cloud-background-functions-have-max-timeout/72350367#72350367
- Working Around the 9 Minute Limit of Google Cloud Functions ↗
- https://stackoverflow.com/questions/52883623/is-there-a-way-that-cloud-function-can-deal-with-long-run-operation
答案2
得分: 0
你似乎在forEach之后返回并发送响应。这会导致函数在后台运行,不能保证在此点之后执行:
后台活动(在函数终止后发生的任何事情)可能会引发问题,因此请检查您的代码。Cloud Functions不保证在函数执行期间以外运行的任何操作,因此即使活动在后台运行,也可能会被清理过程终止。
你可能想在这里使用async/await语法。在forEach中使用的Promise不会按您想的方式执行。最好的方法是使用for..of遍历组。但是,batchWrites一次只允许500个操作,因此您需要一些逻辑来每次只获取500个操作。
// 由于Firestore批量写入限制为500,如果需要,将操作分成多个批次
const batches: WriteBatch[] = [];
let currentBatch = getFirestore().batch();
let operationCounter = 0;
for (const groupDoc of groupsSnapshot.docs) {
if (operationCounter === 500) {
batches.push(currentBatch);
currentBatch = getFirestore().batch();
operationCounter = 0;
}
const newElement = { [`Members.${groupDoc.id}.activities`]:
groupDoc.data().activities.length };
// ... 将newElement添加到批量写操作
// 假设我们有一个文档引用,docRef
// currentBatch.update(docRef, newElement);
operationCounter++;
}
// 如果批次有操作,推送最后一个批次
if (operationCounter > 0) {
batches.push(currentBatch);
}
// 执行所有批量写入
const batchPromises = batches.map((batch) => batch.commit());
try {
await Promise.all(batchPromises);
return "ok";
} catch (error) {
return error;
}
英文:
You seem to be returning and sending back a response after the forEach. This will cause the function to run in the background which can't guarantee execution after this point:
https://cloud.google.com/functions/docs/troubleshooting
> Background activity (anything that happens after your function has terminated) can cause issues, so check your code. Cloud Functions does not guarantee any actions other than those that run during the execution period of the function, so even if an activity runs in the background, it might be terminated by the cleanup process.
You might want to use async/await syntax here. Promises within a forEach don't execute how you would think. Best bet would be to iterate with a for..of over the groups. However, batchWrites only allow 500 operations at a time, so you'll need some logic to slice(0, 500) to only get 500 operations at a time.
// Since Firestore batch write limit is 500, divide operations into
multiple batches if needed
const batches: WriteBatch[] = [];
let currentBatch = getFirestore().batch();
let operationCounter = 0;
for (const groupDoc of groupsSnapshot.docs) {
if (operationCounter === 500) {
batches.push(currentBatch);
currentBatch = getFirestore().batch();
operationCounter = 0;
}
const newElement = { [`Members.${groupDoc.id}.activities`]:
groupDoc.data().activities.length };
// ... add newElement to the batch write operation
// assuming we have a document reference, docRef
// currentBatch.update(docRef, newElement);
operationCounter++;
}
// Push the last batch if it has operations
if (operationCounter > 0) {
batches.push(currentBatch);
}
// Execute all batch writes
const batchPromises = batches.map((batch) => batch.commit());
try {
await Promise.all(batchPromises);
return "ok";
} catch (error) {
return error;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论