Документация Google App Engine содержит этот абзац:
Примечание. Если ваше приложение получает исключение при совершении транзакции, это не всегда означает, что транзакция завершилась неудачно. Вы может принимать DatastoreTimeoutException, ConcurrentModificationException или DatastoreFailureException исключения в случаях совершения сделок и в конечном итоге будут успешно применены. Когда это возможно, сделайте свой Операции хранилища данных идемпотент, чтобы, если вы повторяете транзакцию, конечный результат будет таким же.
Подождите, что? Похоже, что существует очень важный класс транзакций, который просто невозможно сделать идемпотентным, поскольку они зависят от текущего состояния хранилища данных. Например, простой счетчик, как в подобной кнопке. Транзакция должна считывать текущий счетчик, увеличивать его и выписывать счет снова. Если транзакция выглядит "сбой", но НЕ ДЕЙСТВИТЕЛЬНО не работает, и мне нечего сказать об этом на стороне клиента, тогда мне нужно попробовать еще раз, что приведет к одному щелчку, генерирующему два "симпатия". Неужели есть какой-то способ предотвратить это с помощью GAE?
Edit:
похоже, что это проблема, присущая распределенным системам, как и не только Guido van Rossum - см. эту ссылку:
исключение транзакции хранилища данных приложения
Итак, похоже, что разработка идемпотентных транзакций в значительной степени необходима, если вы хотите получить высокую степень надежности.
Мне было интересно, можно ли реализовать глобальную систему в целом приложении для обеспечения идемпотентности. Ключом будет поддерживать журнал транзакций в хранилище данных. Клиент сгенерировал GUID, а затем включил бы этот GUID с запросом (тот же идентификатор GUID будет повторно отправлен на повторные попытки для того же запроса). На сервере в начале каждой транзакции он будет выглядеть в хранилище данных для записи в группе сущностей транзакций с этим идентификатором. Если он найдет это, то это повторная транзакция, поэтому она вернется без каких-либо действий.
Конечно, это потребует включения транзакций между группами или наличия отдельного журнала транзакций в качестве дочернего элемента каждой группы объектов. Также было бы поражение производительности, если поисковые запросы с отсутствием сущностей были медленными, поскольку почти каждая транзакция включала неудачный поиск, поскольку большинство идентификаторов GUID были бы новыми.
Что касается дополнительных $затрат в отношении дополнительных взаимодействий хранилища данных, это, вероятно, все равно будет меньше, чем если бы мне пришлось делать каждую транзакцию идемпотентной, поскольку для этого потребовалось бы много проверки того, что в хранилище данных на каждом уровне.