Путаница по поводу повторных попыток объективирования транзакций - PullRequest
0 голосов
/ 18 января 2020

Объективная документация заявляет об операциях:

Работа ДОЛЖНА быть идемпотентной. Различные условия, в том числе ConcurrentModificationException, могут привести к повторной попытке транзакции. Если вам нужно ограничить количество попыток транзакции, используйте transactNew (int, Work).

Однако в хранилище данных Google документация указывает:

API хранилища данных не автоматически повторяет транзакции, но вы можете добавить свои собственные логи c, чтобы повторить их, например, для обработки конфликтов, когда другой запрос одновременно обновляет ту же сущность .

Эти утверждения кажутся противоречивыми. Действительно ли повторяются транзакции по объекту? Просто чтобы быть в безопасности, я использую transactNew(1, Work), чтобы убедиться, что он запускается только один раз, но что происходит под капотом и почему?

Документация Google гласит, что одно из применений транзакций заключается в выполнении таких вещей, как передача деньги с одного счета на другой. Если транзакции повторяются, то это не сработает, потому что перевод денег по своей природе не идемпотентен. В этом случае использование transactNew(1, Work) - это правильно, правильно ли поступать? По сути, я ищу безопасную транзакцию без идемпотента.

Ответы [ 2 ]

2 голосов
/ 18 января 2020

Objectify повторится на CME. Существует некоторый вопрос относительно того, можете ли вы получить CME, когда транзакция фактически совершается - когда-то было задокументировано, что это возможно, но Google, возможно, устранил это.

Тем не менее, «правильный путь» гарантировать, что (скажем) банковский перевод завершен, не означает ограничение повторных попыток.

  1. Создайте идентификатор транзакции вне повторной попытки. Просто какое-то уникальное значение.
  2. Запустите транзакцию, попробуйте загрузить идентификатор транзакции. Это существует? Ваша транзакция уже завершена.
  3. Если ее не существовало, создайте объект транзакции (с идентификатором) и выполните дебет + кредит.

В конечном итоге это в значительной степени стандартное поведение для любой банковской книги; вы создаете запись транзакции вместе с дебет + кредит. Если вы создаете запись транзакции, легко применять идемпотентность.

1 голос
/ 18 января 2020

Вы просматриваете 2 разные клиентские библиотеки:

  • объект objectify, который включает автоматические c повторов, настраиваемый
  • файл простого хранилища данных, который не включая такие повторы, вам придется самим позаботиться о повторных попытках

Проблема перевода денег не обязательно не идемпотентна, в том смысле, что ее можно сделать идемпотентом с помощью транзакций. Ключ заключается в том, чтобы включить обе модификации учетной записи в одну и ту же транзакцию, как показано в примере клиента хранилища данных :

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();
    }
  }
}

Таким образом, либо обновляются обе учетные записи, либо ни одна из них не является - если транзакция завершается неудачей, все изменения либо не фиксируются, либо откатываются.

FWIW, чтобы проверить мою (ndb) логи повторных попыток транзакции c и идемпотентность I поместила транзакции (с соответствующими сообщениями отладки) в pu sh обработчики очереди задач и одновременно запускали несколько задач, вызывая конфликты. Журналов запросов и приложений было вполне достаточно для проверок.

...