Depository of News

Идемпотентность в платёжном конвейере: «повтори запрос» — это архитектура, а не retry

newsdepo.com · world

<img src="https://habrastorage.org/getpro/habr/upload_files/889/7d8/b38/8897d8b38d5d03a831b50a7d96d51b30.png" /><p>Начну со сцены, которую видел каждый, кто эксплуатировал платёжный сервис хотя бы год.</p><p>Клиент нажимает «Оплатить». Приложение отправляет&nbsp;<code>POST /payments</code>. Проходит пять секунд, и HTTP-клиент падает по таймауту. Ретрай-политика, которую кто-то настроил год назад по гайду, ждёт двести миллисекунд и отправляет запрос ещё раз. Через минуту клиент видит в выписке&nbsp;<strong>два списания</strong>.</p><p>На разборе обычно приходят к одному из двух выводов. Оба неверные, хотя второй я сам когда-то считал правильным.</p><p><strong>Первый вывод: надо выключить retry на POST.</strong>&nbsp;Хорошо, выключили. Теперь у нас платежи, про которые никто не знает, прошли они или нет. Поддержка разбирает их руками. Ущерб никуда не делся, он просто переехал на другой счёт.</p><p><strong>Второй вывод: надо добавить заголовок&nbsp;<code>Idempotency-Key</code>.</strong>&nbsp;Добавили. Дедупликацию сделали в middleware поверх Redis. Через полгода двойное списание случается снова. Потому что Redis пережил failover и потерял ключи. Или потому что повтор пришёл через сутки из файла клиринга, а в файле никакого HTTP-заголовка нет.</p><p>Обе реакции считают идемпотентность свойством одного вызова. На самом деле это свойство&nbsp;<strong>контракта между двумя сторонами</strong>. И контракт должен доходить без потерь через весь конвейер: от кнопки в приложении до проводки в главной книге и дальше, до файла, который уходит в платёжную схему.</p><p>Retry сам по себе всего лишь тактика. Он работает там, где контракт уже есть. Без контракта retry не делает систему устойчивее. Он делает её быстрее ломающейся.</p> <a href="https://habr.com/ru/articles/1080814/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1080814#habracut">Читать далее</a>

Read full article →