7

请帮我找出我的误解。

我正在 App Engine 上编写 RPG。玩家采取的某些行动会消耗一定的统计数据。如果统计数据达到零,则玩家不能再采取任何行动。不过,我开始担心作弊玩家——如果玩家非常快速地发送两个动作,紧挨着对方怎么办?如果减少统计数据的代码不在事务中,则玩家有机会执行该操作两次。所以,我应该将减少统计数据的代码包装在事务中,对吗?到目前为止,一切都很好。

但是,在 GAE Python 中,我们在文档中有这样的内容:

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

哎呀。这意味着我正在运行的函数如下所示:


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

好吧,那是行不通的,因为这东西不是幂等的,对吧?如果我在它周围放置一个重试循环(我需要在 Python 中这样做吗?我已经读过我不需要在 SO 上这样做......但我在文档中找不到它)它可能会将该值增加两次,正确的?由于我的代码可以捕获异常,但数据存储仍然提交了数据……嗯?我该如何解决?这是我需要分布式事务的情况吗?我真的吗?

4

4 回答 4

13

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

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

我想知道“极其罕见”对于 AppEngine 意味着什么(百万分之一,还是十亿分之一?),但我的建议是,幂等交易是财务问题所必需的,但不是游戏得分,甚至是“生命”;-)

于 2012-11-15T05:27:43.547 回答
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)
于 2012-04-15T10:28:50.043 回答
1

您是否应该尝试将此类信息存储在 Memcache 中,这比 Datastore 快得多(如果您的应用程序中经常使用此统计信息,您将需要此信息)。Memcache 为您提供了一个不错的功能decr

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

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

于 2012-04-15T05:19:22.660 回答
1

如果您仔细考虑所描述的内容,它实际上可能不是问题。这样想:

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

以下是可能发生的情况:

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

A1 正常失败(不更改数据)。计划重试。A2然后尝试,成功。当 A1 再次尝试时,它将中止。

A1成功但报错。计划重试。下次 A1 或 A2 尝试时,它们将中止。

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

于 2012-04-15T05:41:34.797 回答