【问题标题】:GAE/P: Transaction safety with API callsGAE/P:API 调用的交易安全
【发布时间】:2019-09-17 00:45:11
【问题描述】:

假设您使用交易来处理 Stripe 支付并更新用户实体:

@ndb.transactional
def process_payment(user_key, amount):
    user = user_key.get()
    user.stripe_payment(amount) # API call to Stripe
    user.balance += amount
    user.put()

Stripe API 调用可能成功,但 put 由于争用而失败。然后会向用户收费,但他的帐户不会反映付款。

您可以将 Stripe API 调用从事务中提取出来,然后再进行事务,但您似乎仍然遇到同样的问题。收费成功,但交易失败,用户账户未记入贷方。

这似乎是一个非常常见的情况。如何正确处理这个问题?

【问题讨论】:

  • 也许你需要实现某种形式的分布式事务,例如两阶段提交 (en.wikipedia.org/wiki/Two-phase_commit_protocol)..?
  • @thebjorn,我会调查的。我希望有人使用 GAE 服务有一个相当简单的解决方案。每个接受付款的人都必须解决这个问题,所以我希望在这个问题上获得更多的活动......

标签: python google-app-engine transactions app-engine-ndb


【解决方案1】:

为了正确操作,事务函数需要是幂等的。所以你不能在这样的函数中进行条带调用,因为它会使它成为非幂等的。

我会单独调用条带,并且在 API 成功后,我会调用一个事务函数来更新用户的帐户余额(在发生争用时可以安全地重试)。

甚至可以创建一个单独的、独立的实体来反映条带 API 调用结果?这样的实体应该没有争用的余地,因为它只被写入一次——当条带事务发生时。这将允许您:

  • 保留帐户交易历史记录 - 指向这些实体
  • 进行一些健全性检查以查找孤立条带交易(如果出于某种原因,即使在重试后帐户交易调用仍失败)并对此采取措施。

@thebjorn 的评论很好:多步骤的方法可以使这个过程非常可靠:

  • 一个交易函数更新帐户以执行条带交易,这也是transactionally enqueues a push task 执行条带 API 调用。仅当事务成功时任务才入队
  • 推送队列任务进行条带 API 调用(最终创建条带事务实体),并在成功时调用事务函数来更新帐户余额

【讨论】:

  • Stripe 具有允许其 API 调用是幂等的功能(您在调用中包含 UUID)。
  • 我不明白最后一个要点。假设更新账户余额的交易失败?那么我们不知道Stripe API调用是否成功。
  • 最后一点 - 更新账户余额的调用只有在条带调用成功时才会发生,所以你知道它是成功的:) 但是使用条带 API 幂等,你可以简单地重试这对 (条带调用加上帐户余额更新),直到两者都成功。
  • 谢谢。好的建议。上述的一个缺点是任务有时执行起来很慢。最好避免任务以帮助确保更快的数据存储更新。
  • 您不需要具有条带 API 幂等性的任务 - 您只需要调整现有的事务函数来处理 UUID。如果事务失败,将重试整个函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-13
  • 2013-07-17
  • 2012-07-29
相关资源
最近更新 更多