firebase - 在 Cloud Function 的一个功能中执行数千次读取和删除 Firestore 操作是否可以?
问题描述
我有一个events
作为父集合的Attendee
子集合,用于记录将参加活动的所有用户,如下图所示。Attendee
子集合包含用户数据
然后还有users
作为父集合的attendedEvents
子集合来记录用户将访问的所有事件,如下图所示。AttendEvents 子集合事件数据。
我使用非规范化,所以似乎事件数据在attendedEvents
这样的子集合中重复
然后我使用云功能做了一个 cron 工作。此 cron 作业任务是评估事件是否已通过(过期)。如果事件已经通过,那么这个函数应该:
- 将事件数据的字段从 isActive == true 更新为 isActive == false
- 读取所有
Attendee
过期事件中的所有文档,获取所有参加者ID,然后删除attendedEvents
用户集合子集合中的所有事件数据。
如您所见,我的 cron 作业功能的第二个任务可能需要读取大约 50.000 - 100.000 个文档,然后还需要删除大约 50.000 - 100.000 个文档作为最坏的情况(峰值)。
所以我的问题是,像这样在 Cloud Function 的一个函数中执行数千次读取和删除操作是否可以?
我担心有一个我不知道的限制。我不确定,有没有我没有考虑过的事情?有没有更好的方法呢?
这是我的云功能代码:
exports.cronDeactivatingExpiredEvents = functions.https.onRequest(async (request,response) => {
const now = new Date()
const oneMonthAgo = moment().subtract(1,"month").toDate()
try {
const expiredEventsSnapshot = await eventRef
.where("isActive","==",true)
.where("hasBeenApproved","==",true)
.where("dateTimeStart",">",oneMonthAgo)
.where("dateTimeStart","<",now)
.get()
const eventDocumentsFromFirestore = expiredEventsSnapshot.docs
const updateEventPromises = []
eventDocumentsFromFirestore.forEach(eventSnapshot => {
const event = eventSnapshot.data()
const p = admin.firestore()
.doc(`events/${event.eventID}`)
.update({isActive: false})
updateEventPromises.push(p)
})
// 1. update isActive to be false in firestore document
await Promise.all(updateEventPromises)
console.log(`Successfully deactivating ${expiredEventsSnapshot.size} expired events in Firestore`)
// getting all attendeeIDs.
// this may need to read around 50.000 documents
const eventAttendeeSnapshot = await db.collection("events").doc(eventID).collection("Attendee").get()
const attendeeDocuments = eventAttendeeSnapshot.docs
const attendeeIDs = []
attendeeDocuments.forEach( attendeeSnapshot => {
const attendee = attendeeSnapshot.data()
attendeeIDs.push(attendee.uid)
})
// 3. then delete expired event in users subcollection.
// this may need to delete 50.000 documents
const deletePromises = []
attendeeIDs.forEach( attendeeID => {
const p = db.collection("users").doc(attendeeID).collection("attendedEvents").doc(eventID).delete()
deletePromises.push(p)
})
await Promise.all(deletePromises)
console.log(`successfully delete all events data in user subcollection`)
response.status(200).send(`Successfully deactivating ${expiredEventsSnapshot.size} expired events and delete events data in attendee subcollection`)
} catch (error) {
response.status(500).send(error)
}
})
解决方案
您必须注意这里的几件事。
1)云功能方面有一些限制。根据您使用正在读取的数据的方式,您可能会达到的配额是出站套接字数据,它是 10GB/100 秒,不包括 HTTP 响应数据。如果您达到此配额,您可以通过转到IAM & admin >> Quotas >> Edit Quotas
并选择来请求增加配额Cloud Function API (Outgoing socket traffic for the Region you want)
。
但是,还有540 秒的最大功能持续时间。我相信你所描述的应该不会花那么长时间。如果确实如此,那么如果您要提交批量删除,即使您的功能由于超过持续时间而失败,也会完成删除。
2)在 Firestore 方面,您也有一些限制。在这里,您可以了解处理读/写操作和高读、写和删除率时的一些最佳实践。如果您尝试以高速率删除按字典顺序关闭的文档,则根据数据的结构和类型,您可能会遇到一些问题,例如连接错误。
另请记住,Firestore对每个付款计划的读/写操作数量有更通用的配额。
无论如何,即使有最好的计算,也总是有出错的余地。所以我的建议是尝试一个你所期望的最高峰的测试场景。如果您达到任何配额,您可以请求增加配额,或者如果您达到任何硬性限制,您可以联系 Google Cloud Platform 支持,提供有关您的项目和用例的具体详细信息。
推荐阅读
- python - Python:获取两个字符串并仅返回完全匹配的字符和索引
- javascript - Ace 编辑器光标错误
- javascript - 在使用反应测试库中的反应测试库测试我的包含 i18next 库的组件时,将错误显示为“无法读取 null 的属性‘等待’”
- pine-script - Pine Script - 如何使用日期时间选择器选择烛台条并在此条的高点和低点绘制水平线
- reactjs - 如何在没有初始状态的情况下使用 combineReducers 创建 Redux 存储
- node.js - 无法解析依赖树,我不再可以使用节点包管理器安装包
- bash - 列值分布到多行
- centos - openstack 实例上没有磁盘
- javascript - 使用 onClick 事件更改身体图像
- r - 从通过重复标点字符连接的字符串的列中提取数字