Firebase更新文档字段,但预计不会这样做(使用事务)

时间:2019-10-11 09:57:05

标签: python python-3.x firebase transactions google-cloud-firestore

对于我的网站,我将每个用户的通知存储在UserDocument中。 每个UserDocument都有一个名为notifications的集合。 在此集合中,有多个NotificationDocuments

users/<uid>/notifications/<notification_id>

NotificationDocument:
message: str
is_read: bool
generated_id: str

通知可能是:“您对评论X有2条回复”

在用户评论中发布其他答复时,SAME NotificationDocument将更新为:

“您的评论X有3条回复”。

现在,NotifcationDocument的状态为is_read。默认为is_read=False。 但是,当用户阅读通知时,此NotifcationDocument设置为`is_read = True。

然后是比赛条件。 如果用户希望将其通知标记为已读,但同时又输入另一个通知,将内容更新为“您对评论X有4条回复”,该怎么办。 如果NotificationDocument在用户希望将其标记为is_read=True的时间内更改了,我想跳过更新。

所以,我认为,让我们使用Firebase事务: https://cloud.google.com/firestore/docs/manage-data/transactions

这是我将NotificationDocument标记为已读的代码:

    def mark_notification_as_read(
        self, notification_id: str, expected_generated_id: str, uid: str
    ) -> None:
        """
        Mark a notification with given `notification_id` as read, but only if it hasn't been updated in the meanwhile.
        This is done by checking if `generated_id` is still the same.
        """
        path =  f"users/{uid}/notifications"
        transaction = self.client.transaction()
        notification_ref = self.client.collection(path).document(notification_id)

        @firestore.transactional
        def update_in_transaction(transaction, notification_ref):
            snapshot = notification_ref.get(transaction=transaction)
            time.sleep(10)
            found_generated_id = snapshot.get('generated_id')

            if found_generated_id == expected_generated_id:
                transaction.update(notification_ref, {
                    'is_read': True,
                })
            else:
                # Log for now, to be able to monitor if this is handled well
                logger.info(msg="Can't mark notification as read as it has been updated!")

        update_in_transaction(transaction, notification_ref)

请注意,在time.sleep(10)内,我在Firebase控制台中将generated_id更新为新值。我希望此交易将失败。 但是,经过那10秒钟后,我看到通知仍然标记为is_read = True。

我在做什么错了?

1 个答案:

答案 0 :(得分:1)

经过一些搜索,我没有使用事务,而是使用write_option解决了我的特定问题。

基本上,我收到快照的update_time(Firebase给出的字段),并且在执行更新时,将其作为write_option传递。说明:如果在更新时last_update不相同,则使update操作失败。

    from google.api_core.exceptions import FailedPrecondition

    [...]
        path = f"users/{uid}/notifications"
        notification_ref = self.client.collection(path).document(id)
        snapshot = notification_ref.get()

        if snapshot.exists:
            if snapshot.get("generated_id") == generated_id:
                write_option = self.client.write_option(
                    last_update_time=snapshot.update_time
                )
                try:
                    notification_ref.update({"is_read": True}, option=write_option)
                except FailedPrecondition:
                    pass
     [...]
相关问题