我有一个events
作为父集合,该子集合具有Attendee
子集合,用于记录将参加活动的所有用户,如下图所示。 Attendee
子集合包含用户数据
,然后将users
作为父集合,该子集合具有attendedEvents
子集合,以记录用户将要访问的所有事件,如下图所示。 relatedEvents的子集合事件数据。
我使用非规范化,因此似乎事件数据在attendedEvents
子集合中是重复的
然后我使用云功能进行了cron作业。此Cron工作任务是评估事件是否已通过(过期)。如果事件已通过,则此函数应:
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)
}
})
答案 0 :(得分:0)
您必须在这里注意几件事。
1)云功能方面有一些限制。根据您使用的读取数据的方式,您可能会达到的配额为Outbound Socket Data,该配额为10GB / 100秒(不包括HTTP响应数据)。如果您达到此配额,可以通过转到IAM & admin >> Quotas >> Edit Quotas
并选择Cloud Function API (Outgoing socket traffic for the Region you want)
来请求增加配额。
但是,还有Maximum function duration(540秒)。我相信您所描述的内容不会花那么长时间。如果确实如此,那么即使您要提交批处理删除,即使您的功能由于持续时间过长而导致功能失败,也会执行删除操作。
2)在Firestore方面,您也有一些限制。在这里,您可以了解有关处理Read/Write operations和High read, write, and delete rates的一些最佳做法。根据数据的结构和类型,如果您尝试以高速率删除按字典顺序关闭的文档,则可能会遇到诸如连接错误之类的问题。
还请记住,每个付款计划的读/写操作次数比较通用Firestore quotas。
无论如何,即使进行了最佳的计算,也总是存在错误的余地。因此,我的建议是尝试一个您期望的最高峰值的测试方案。如果您达到任何配额,则可以请求增加配额,或者如果达到任何严格限制,则可以与Google Cloud Platform支持联系,以提供有关项目和用例的特定详细信息。
答案 1 :(得分:-1)
是的,没关系,但不是您尝试的方式。通常,您不得在循环中进行读取/写入/删除操作,而必须在“事务”或“批量写入”中进行。在此处查找有关这些内容:Transactions and batched writes
此外,每笔交易限制为500个文档,因此您可以在多个交易中中断整个操作。例如,如果要删除2000个文档,请将它们添加到4个单独的事务中并执行这些事务。