GAE / P: безопасность транзакций с вызовами API - PullRequest
8 голосов
/ 28 апреля 2019

Предположим, вы используете транзакцию для обработки платежа 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()

Возможно, вызов API Stripe завершился успешно, но put завершился неудачей из-за конфликта. После этого с пользователя будет снята оплата, но его учетная запись не будет отражать платеж.

Вы можете извлечь вызов Stripe API из транзакции и выполнить транзакцию впоследствии, но, похоже, у вас все еще остается та же проблема. Заряд успешно завершен, но транзакция не выполнена, а учетная запись пользователя не зачислена.

Это похоже на очень распространенный сценарий. Как правильно с этим справиться?

1 Ответ

2 голосов
/ 06 мая 2019

Для правильной работы транзакционная функция должна быть идемпотентной. Таким образом, вы не можете сделать вызов чередования внутри такой функции, так как это сделает ее неидемпотентной.

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

Может быть, даже создать отдельную, независимую сущность, отражающую результат вызова API полосы? Такая сущность должна не иметь места для разногласий, поскольку она записывается только один раз - когда происходит полосовая транзакция. Это позволит вам:

  • вести историю операций по счетам - указывая на эти объекты
  • проведет несколько проверок работоспособности для поиска транзакции с несвязанными полосами (если по какой-либо причине вызов транзакции учетной записи завершится неудачно даже после повторных попыток) и что-то с этим сделает.

@ Комментарий thebjorn хорош: многошаговый подход может сделать процесс довольно солидным:

  • транзакционная функция, обновляющая учетную запись с намерением выполнить полосовую транзакцию, которая также транзакционно ставит в очередь push-задачу для выполнения полосового API-вызова. Задача ставится в очередь, только если транзакция завершится
  • задача push-очереди выполняет вызов API чередования (в конечном итоге создает объект транзакции чередования) и, в случае успеха, вызывает транзакционную функцию для обновления баланса счета
...