App Engine,交易和幂等性

时间:2012-04-15 04:14:54

标签: python google-app-engine transactions

请帮助我找出我的误解。

我在App Engine上写了一个RPG。玩家采取的某些行动会消耗一定的数据。如果统计数据达到零,则玩家不再采取行动。不过,我开始担心会欺骗球员 - 如果一名球员很快就发出了两个动作,那就紧挨着呢?如果递减stat的代码不在事务中,则玩家有机会执行两次动作。所以,我应该包装在事务中减少stat的代码,对吧?到目前为止,非常好。

在GAE Python中,我们在documentation

中有这个
  

注意:如果您的应用在提交交易时收到异常,则不会   总是意味着交易失败。您可以收到Timeout,TransactionFailedError或   在事务已提交且最终将发生的情况下的InternalError异常   成功应用。只要有可能,请使您的数据存储区事务具有幂等性   如果你重复交易,最终结果将是相同的。

糟糕。这意味着我运行的函数看起来像这样:


def decrement(player_key, value=5):
  player = Player.get(player_key)
  player.stat -= value
  player.put()

嗯,这不会起作用,因为事情不是幂等的,对吧?如果我在它周围放置一个重试循环(我是否需要在Python中?我已经读过我不需要在SO上...但我在文档中找不到它)它可能会增加两次值,对?由于我的代码可以捕获异常,但数据存储仍然提交数据......呵呵?我该如何解决?这是我需要distributed transactions的情况吗?我真的吗?

4 个答案:

答案 0 :(得分:13)

首先,尼克的回答是不正确的。 DHayes的事务不是幂等的,所以如果它被多次运行(即,当第一次尝试被认为失败时重试,当它没有时),那么该值将被多次递减。 Nick说“数据存储区会检查实体是否已经被修改,”但这并不能解决问题,因为这两个事务具有单独的提取,第二次提取是在第一个事务完成之后。

要解决此问题,您可以通过创建“事务密钥”并将该密钥作为事务的一部分记录在新实体中来使事务具有幂等性。第二个事务可以检查该事务密钥,如果找到,将不执行任何操作。一旦您对交易完成感到满意,或者您放弃重试,就可以删除交易密钥。

我想知道AppEngine(百万分之一,或十亿分之一?)的“非常罕见”是什么意思,但我的建议是,财务问题需要进行幂等交易,但不是游戏分数,甚至不是“生命”; - )

答案 1 :(得分:4)

编辑:这是不正确的 - 请参阅评论。

你的代码很好。文档所指的幂等性是关于副作用的。正如文档所解释的那样,您的事务功能可能会运行多次;在这种情况下,如果该功能有任何副作用,它们将被多次应用。由于你的交易功能没有那么做,所以没关系。

关于幂等性的问题函数的一个例子是这样的:

def do_something(self):
  def _tx():
    # Do something transactional
    self.counter += 1
  db.run_in_transaction(_tx)

在这种情况下,self.counter可能会增加1,或者可能会增加1.这可以通过在事务外部执行副作用来避免:

def do_something(self):
  def _tx():
    # Do something transactional
    return 1
  self.counter += db.run_in_transaction(_tx)

答案 2 :(得分:1)

你不应该尝试在Memcache中存储这种信息,这比数据存储快得多(如果在你的应用程序中使用这个数据,你需要的东西)。 Memcache为您提供了一个很好的功能:decr

  

以原子方式递减键值。在内部,该值是无符号的64位整数。 Memcache不检查64位溢出。如果值太大,将会回滚。

搜索decr here。然后,您应该使用任务将此密钥中的值保存到数据存储区,每隔x秒或满足特定条件。

答案 3 :(得分:1)

如果你仔细考虑你所描述的内容,实际上可能不是一个问题。以这种方式思考:

你的玩家剩下一个统计点。然后,他立即恶意发送2个动作(A1和A2),每个动作都需要消耗该点。 A1和A2都是交易性的。

以下是可能发生的事情:

A1成功。然后A2将中止。一切都好。

A1合法失败(不更改数据)。重试预定。 A2然后尝试,成功。当A1再次尝试时,它将中止。

A1成功但报告错误。重试预定。下次A1或A2尝试时,它们将中止。

为了实现这一点,你需要跟踪A1和A2是否已经完成 - 也许给他们一个任务UUID并存储已完成任务的列表?甚至只是使用任务队列。

相关问题