对于我的网站,我将每个用户的通知存储在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。
我在做什么错了?
答案 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
[...]