简体   繁体   English

关于对象化事务重试的困惑

[英]Confusion about objectify transactions retrying

The objectify documentation states this about transactions: objectify文档说明了有关交易的内容:

Work MUST be idempotent.工作必须是幂等的。 A variety of conditions, including ConcurrentModificationException, can cause a transaction to retry.包括 ConcurrentModificationException 在内的各种条件都可能导致事务重试。 If you need to limit the number of times a transaction can be tried, use transactNew(int, Work).如果您需要限制可以尝试事务的次数,请使用 transactNew(int, Work)。

However, the google datastore documentation states:但是,谷歌数据存储 文档指出:

The Datastore API does not automatically retry transactions, but you can add your own logic to retry them, for instance to handle conflicts when another request updates the same entity at the same time.数据存储区API不会自动重试交易,但你可以添加自己的逻辑来进行重试,例如处理冲突时,另一个请求更新在同一时间同一实体。

These statements seem contradictory.这些说法似乎自相矛盾。 Are objectify transactions really retried? objectify 交易真的重试了吗? Just to be safe I am using transactNew(1, Work) to make sure it runs only once, but what is going on under the hood and why?为安全起见,我使用transactNew(1, Work)来确保它只运行一次,但是引擎盖下发生了什么,为什么?

The google documentation states that one use of transactions is to do things like transfer money from one account to another.谷歌文档指出,交易的一种用途是将资金从一个帐户转移到另一个帐户。 If transactions are retrying then that would not work because by nature transferring money is not idempotent.如果交易正在重试,那么这将不起作用,因为转移资金本质上不是幂等的。 In this case, using transactNew(1, Work) is the correct thing to do right?在这种情况下,使用transactNew(1, Work)是正确的做法吗? Basically, I am looking to do a non-idempotent transaction safely.基本上,我希望安全地进行非幂等交易。

Objectify will retry on CME. Objectify 将在 CME 上重试。 There is some question as to whether you can get a CME when the transaction actually commits - once upon a time it was documented that this was possible, but Google may have eliminated that.关于在交易实际提交时您是否可以获得 CME 存在一些问题——曾几何时有记录表明这是可能的,但谷歌可能已经消除了这一点。

Nevertheless, the "right way" to ensure a (say) bank transfer completes is not to limit the retries.然而,确保(比如)银行转账完成的“正确方法”不是限制重试。

  1. Create a transaction id outside of the retry.在重试之外创建一个事务 ID。 Just some unique value.只是一些独特的价值。
  2. Start your transaction, attempt to load that transaction id.开始您的交易,尝试加载该交易 ID。 Does it exist?它存在吗? Your transaction is already complete.您的交易已经完成。
  3. If it didn't exist, create your transaction object (with the id) and do the debit + credit.如果它不存在,请创建您的交易对象(带有 id)并进行借记 + 贷记。

This ends up being pretty much standard behavior for any bank-like ledger;这对于任何类似银行的分类账来说都是非常标准的行为; you create a transaction record along with the debit+credit.您与借记+贷记一起创建交易记录。 If you create a transaction record, it is easy to enforce idempotence.如果您创建交易记录,则很容易强制执行幂等性。

You are looking at 2 different client libraries:您正在查看 2 个不同的客户端库:

  • the objectify one, which appears to be including automatic retries, customizable objectify 一个,它似乎包括自动重试,可定制
  • the plain datastore one which does not include such retries, you'd have to take care of the retries yourself不包含此类重试的普通数据存储区,您必须自己处理重试

The money transfer problem isn't necessarily not idempotent, in the sense that it can be made idempotent using transactions.汇款问题不一定是幂等的,因为它可以使用交易使其成为幂等的。 The key is to include both account modifications inside the same transaction, as shown in the datastore client example :关键是在同一事务中包含两个帐户修改,如数据存储客户端示例所示

void transferFunds(Key fromKey, Key toKey, long amount) {
  Transaction txn = datastore.newTransaction();
  try {
    List<Entity> entities = txn.fetch(fromKey, toKey);
    Entity from = entities.get(0);
    Entity updatedFrom =
        Entity.newBuilder(from).set("balance", from.getLong("balance") - amount).build();
    Entity to = entities.get(1);
    Entity updatedTo = Entity.newBuilder(to).set("balance", to.getLong("balance") + amount)
        .build();
    txn.put(updatedFrom, updatedTo);
    txn.commit();
  } finally {
    if (txn.isActive()) {
      txn.rollback();
    }
  }
}

This way either both accounts are updated, or none of them is - if the transaction fails all changes are either not committed or rolled back.这样,要么两个帐户都更新,要么都不更新——如果事务失败,则所有更改要么不提交,要么回滚。

FWIW, to verify my ( ndb -based) transaction retry logic and idempotence I placed the transactions (with relevant debug messages) in push task queue handlers and triggered multiple tasks simultaneously to cause conflicts. FWIW,为了验证我的(基于ndb的)事务重试逻辑和幂等性,我将事务(带有相关调试消息)放在推送任务队列处理程序中,并同时触发多个任务以引起冲突。 The request and app logs were fairly sufficient for the verifications.请求和应用程序日志足以进行验证。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM