我遇到了一个小困境!我有一个名为投票的处理程序;当它被调用时,它将用户的投票设置为他们所选择的任何内容。为了记住他们之前选择的选项,我存储了一个VoteRecord选项,详细说明了他们当前的投票设置。
当然,他们第一次投票时,我必须创建对象并存储它。但连续投票只应改变现有VoteRecord的价值。但他提出了这个问题:在某些情况下可以创建两个VoteRecords。这种情况很罕见(到目前为止我们所看到的所有500张选票中只发生过一次),但它确实很糟糕。
问题出现是因为两个独立的处理程序基本上都是这样做的:
query = VoteRecord.all().filter('user =', session.user).filter('poll =', poll)
if query.count(1) > 0:
vote = query[0]
poll.votes[vote.option] -= 1
poll.votes[option] += 1
poll.put()
vote.option = option
vote.updated = datetime.now()
vote.put()
else:
vote = VoteRecord()
vote.user = session.user
vote.poll = poll
vote.option = option
vote.put()
poll.votes[option] += 1
poll.put()
session.user.votes += 1
session.user.xp += 3
session.user.put()
incr('votes')
我的问题是:在确保没有请求丢失且没有请求创建两个VoteRecord对象的情况下,处理这些请求的最有效和最快捷的方法是什么?
答案 0 :(得分:3)
最简单的方法是使用投票对象的键名,并使用Model.get_or_insert。首先,为您的密钥名称提出一个命名方案 - 在民意调查之后将其命名为一个好主意 - 然后执行get_or_insert以获取或创建相关实体:
vote = VoteRecord.get_or_insert(pollname, parent=session.user, user=session.user, poll=poll, option=option)
if vote.option != option:
# Record already existed; we need to update it
vote.option = option
vote.put()
答案 1 :(得分:1)
问题在于这一部分:
if vote.count(1) == 0:
obj = VoteRecord()
obj.user = user
obj.option = option
obj.put()
如果没有事务,您的代码可以在两个解释器实例中按此顺序运行:
if vote.count(1) == 0:
obj = VoteRecord()
obj.user = user
if vote.count(1) == 0:
obj = VoteRecord()
obj.user = user
obj.option = option
obj.put()
obj.option = option
obj.put()
或任何奇怪的组合。问题是计数测试在put发生之前再次运行,所以第二个线程遍历条件的第一部分而不是第二部分。
您可以通过将代码放入函数然后使用
来解决此问题db.run_in_transaction()
运行该功能。
问题是您似乎依赖于查询返回的对象计数,这些对象需要放入事务中。如果您阅读Google I / O会谈或查看该论坛,您会发现不建议这样做。那是因为您无法对查询进行事务处理。相反,您应该将计数存储在某个地方的实体值,在事务函数的外部中查询,然后将该实体的密钥传递给您的事务函数。
以下是检查实体属性的事务函数示例。它将密钥作为参数传递:
def checkAndLockPage(pageKey):
page = db.get(pageKey)
if page.locked:
return False
else:
page.locked = True
page.put()
return True
一次只有一个用户可以锁定此实体,并且永远不会有任何重复的锁。