3

我遇到了一个小难题!我有一个叫 vote 的处理程序;当它被调用时,它将用户的投票设置为他们选择的任何内容。为了记住他们之前选择的选项,我存储了一个 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 对象的同时,处理这些请求的最有效和最快的方法是什么?

4

2 回答 2

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()
于 2009-05-31T15:29:25.770 回答
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

一次只有一个用户可以锁定这个实体,并且永远不会有任何重复的锁定。

于 2009-02-07T03:19:59.627 回答