在 Cloud Function 的一个功能中执行数千次读取和删除 Firestore 操作是不是可以?

Posted

技术标签:

【中文标题】在 Cloud Function 的一个功能中执行数千次读取和删除 Firestore 操作是不是可以?【英文标题】:Is it OK to perform thousand of read and delete Firestore operations in one function of Cloud Function?在 Cloud Function 的一个功能中执行数千次读取和删除 Firestore 操作是否可以? 【发布时间】:2019-12-12 07:34:11 【问题描述】:

我有一个events 作为父集合,它有Attendee 子集合来记录所有将参加活动的用户,如下图所示。 Attendee 子集合包含用户数据

然后还有users 作为父集合,它有attendedEvents 子集合来记录用户将访问的所有事件,如下图所示。 AttendedEvents 子集合事件数据。

我使用非规范化,因此事件数据似乎在 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)
        
)

【问题讨论】:

这里是您的云函数配额值...cloud.google.com/functions/quotas ...从这些您将看到函数的持续时间和函数的最大内存有上限。测试时,请尝试确定执行的持续时间和内存需求。 @Kolban 非常感谢您提供的信息。问题是,我不知道如何估计最坏情况下删除和读取 100.000 个文档的执行时间,我仍处于开发阶段。所以我不确定我是否已经达到了 540 秒的执行时间 /events/someEventId/Attendee的数据结构是什么?文档中存储的uid是不是和文档id一样? 参加者的文档实际上是一个用户数据,其中包含userUID作为文档的字段。所以 eventID 和 userUID 不一样 【参考方案1】:

这里有几点需要注意。

1) Cloud Function 方面存在一些限制。根据您使用正在读取的数据的方式,您可能会达到的配额是Outbound Socket Data,即 10GB/100 秒,不包括 HTTP 响应数据。如果您达到此配额,您可以通过转到IAM &amp; admin &gt;&gt; Quotas &gt;&gt; Edit Quotas 并选择Cloud Function API (Outgoing socket traffic for the Region you want) 来请求增加配额。

不过,也有 540 秒的Maximum function duration。我相信你所描述的应该不会花那么长时间。如果确实如此,那么如果您要提交批量删除,即使您的函数由于超过持续时间而失败,也会执行删除。

2) 在 Firestore 方面,您也有一些限制。在这里,您可以了解处理Read/Write operations 和High read, write, and delete rates 时的一些最佳实践。如果您尝试快速删除按字典顺序关闭的文档,可能会遇到一些问题,例如连接错误,具体取决于数据的结构和类型。

还要记住更通用的Firestore quotas 对每个付款计划的读/写操作次数。

无论如何,即使有最好的计算,也总会有出错的余地。所以我的建议是尝试一个你所期望的最高峰的测试场景。如果您达到任何配额,您可以请求增加配额,或者如果您达到任何硬​​性限制,您可以联系 Google Cloud Platform 支持,提供有关您的项目和用例的具体详细信息。

【讨论】:

以上是关于在 Cloud Function 的一个功能中执行数千次读取和删除 Firestore 操作是不是可以?的主要内容,如果未能解决你的问题,请参考以下文章

Cloud Function 将功能部署到所选项目

无法在安全的Google Cloud Function上获得CORS错误

从 Cloud Function (python) 写入 Google Cloud Storage

如何在 Angular 组件中正确使用 Cloud Functions?

通过 API Key 访问 Google Cloud Function

spring cloud function 函数接口返回成功/失败处理