我有这个模式来存储用户,他们的消息,以及他们有多少条未读消息的计数。
from google.appengine.ext.ndb import *
class User(Model):
unread_messages = IntegerProperty()
class Message(Model):
read = BooleanProperty()
user_id = IntegerProperty()
我的目标是让messages
在用户阅读完一些消息后包含正确的值。创建消息时,很容易使用事务将unread_messages属性增加一个并继续。但阅读信息似乎更加困难。
问题是delta来自一个查询,它可能在写完成之前两次返回相同的结果。
#User reads messages
query = Message.query()
query = query.filter(Message.read = False)
query = query.filter(Message.user_id = user.key.id())
unread_messages = query.fetch(10)
for message in unread_messages:
message.read = True
put_multi(unread_messages)
txn(user.key.id(), -len(unread_messages))
@run_in_transaction
def txn(id, delta):
user = Key(User, id).get()
user.unread_messages += delta
user.put()
据我所知,在保证在查询中显示写入后,无法执行代码。所以对于这种方法,我只是在任务上设置了几秒的延迟。这种方法大部分时间都有效,但很容易看出写入时间超过延迟时间会导致值不正确。
query = Message.query()
query = query.filter(Message.read = False)
query = query.filter(Message.user_id = user.key.id())
unread_messages = query.fetch(10)
for message in unread_messages:
message.read = True
put_multi(unread_messages)
taskqueue.add(url = '/tasks/update-unread-messages', params = {'user_id': user.key.id()}, countdown = 10)
相关任务:
query = Message.query()
query = query.filter(Message.read = False)
query = query.filter(Message.user_id = user.key.id())
count = query.count()
user.unread_messages = count
user.put()
@run_in_transaction
def txn(id, delta):
user = Key(User, id).get()
user.unread_messages += delta
user.put()
为用户的消息提供相同的祖先,以便我可以进行祖先查询。由于祖先查询的性能限制以及我不想替换每个实体这一事实,这将是最后的手段。
将事务标志与taskqueue一起使用。虽然我的put_multi是事务性的,但我需要使用祖先查询。
以各种方式重构架构,但一旦我知道put已经完成,它总是能够运行代码。
答案 0 :(得分:1)
更新将要读取的所有消息并更新计数器会感觉很昂贵。为什么不使用具有未读消息的密钥的实体快速读取(按键)并仅更新单个用户实体,该实体具有所有未读消息的索引和计数。
class User(ndb.Model):
unread_messages_count = ndb.IntegerProperty(default=0)
unread_messages_index = ndb.KeyProperty(repeated=True)
此未读消息索引仅在消息按到达时的顺序读取时才有效。见下面的评论。