Как стать автором
Обновить

Комментарии 18

Стоило бы здесь употребить термин идемпотентность, применительно к запросам у некоторых систем (юкасса, например) есть ключ идемпотентности в качестве параметра, в терминах статьи это как раз ID запроса - цели и логика его применения аналогична.

Также в статье упоминается проблема конкурентных запросов (race condition), но в итоговом блоке она уже не рассматривается. Было бы полезно почитать и про решение этой проблемы, особенно при заявленном масштабе в планету

Да, про конкурентные запросы тоже хорошая тема для статьи и есть что рассказать. Очень вероятно что напишу статью про то как там решать проблемы консистентности.

На самом деле для решения задачи именно идемпотентность не нужна (и избыточно), достаточно безопасной повторяемости. И много неидемпотентных запросов являются вполне себе безопасно повторяемыми, например, getNextSequenceId.
Но, конечно, тема безопасного повтора транзакций гораздо шире описанного в статье.

А не рациональнее ли FIX использовать - старый и выверенный протокол используемый всеми биржами мира, ну кроме крипто, да и тут вопрос времени. Для fix разработанныскоростные решения на cpp и отлажены механизмы обработки сбоев.

Не во всех кейсах применимо, в интернете победил HTTP. А где-то и JSON-RPC. В протоколах где проблема решена на уровне самого протокола - всё хорошо, а для остальных есть решение из статьи.

FIX все-таки для очень специфических задач используется. Для финансовых транзакций все несколько сложнее, есть куча разных протоколов, есть ISO 8583, есть ISO 20022, есть OpenBanking, но для реальных приложений они все избыточно универсальные, требуется их приземлять на конкретные кейсы.
Но вообще для платежей обычно вместо ключей идемпотентности (описанных в статье) используются схема из двух шагов (авторизация и подтверждения), которые не сложнее в реализации, но дают и другие возможности.

авторизация и подтверждения

Вы не про двухстадийную оплату? Это же обычно не связано с описанной в статье проблемой, а продиктовано бизнес-логикой.

Для каждой из стадий (авторизация/подтверждение) проблема исполнения запроса до конца актуальна

А почему двустадийка продиктована бизнес-логикой? Все то же самое можно сделать и в одну операцию (и, редко, так и делают), но тогда не будет как раз идемпотентности в распределенной системе.
А каждая из стадий вполне себе безопасная для повторений (авторизации можно повторять много раз, реально же никакие деньги не снимаются при этом, только блокируются на какое-то время, к тому же авторизацию можно отменить; подтверждение всегда безопасно повторяемо).
Вообще, для безопасных транзакций есть много разных паттернов:
- ключ идемпотентности
- двухстадийные операции
- возможность проверить статус платежа
Все они решают проблемы, описанные в статье. Все имеют некоторую стоимость. Ключ идемпотентности - самое дорогое решение по железу, но самое простое в реализации.

А почему двустадийка продиктована бизнес-логикой?

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

По крайней мере в екоме я еще не встречал кейсов, когда двухстадийку выбирают ради решения проблемы с http запросами.

В остальном согласен, да. Эквайринг это одна из тех сфер, когда описанная в статье проблема актуальна и имеет целый набор вариантов решения.

Ну, да, в ecom нужно еще и товар на складе заблокировать и это лучше всего делать между авторизацией средств и подтверждением (так как в этот момент дешевле всего отменить платеж).
Но почти во всех сценариях получается, что двухстадийка удобнее, нежели ключ идемпотентности, поэтому ключ и используется относительно редко.

Использование ключа наоборот значительно дешевле.

Двухстадийный запрос на первый взгляд выглядит отличным решением - ведь и правда можно вызвать сколько угодно безопасно, казалось бы всё хорошо. Только вот чтобы вторая стадия стала валидной - нужно чтобы бекенд знал о том что первая стадия прошла успешо. Некий стейт, например - ключ транзакции… И получается что кеш ключей нам всё равно нужен, только вместо одного запроса теперь нам нужно будет отправить два, при этом дождаться ответа от первого, а это тоже не нулевое время.

В итоге двухстадийный метод не имеет плюсов и имеет минусы затрат на время, трафик и последовательные операции.

Кэш ключей тоже требует персистанса (так как неизвестно, на какой экземпляр прилетит повтор запроса), так что ключ идемпотентности не дает никаких преимуществ в реализации.
Но при этом двухстадийная транзакция сильно упрощает реализацию более сложных транзакций (например, реализацию оплаты одного товара с нескольких источников средств или проверку состояния получателя и прочих сценариев).
Поэтому в реальной жизни, обычно, предпочитают двухстадийные транзакции, а не ключи идемпотентности. И именно поэтому двухстадийный подход является стандартом "де-факто" для финтеха.

Отправляем запрос на 20 000 000 евро, на перестановку 900 ордеров на бирже

Не надо даже спрашивать: "в чем сила? "

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

Сверху можно вам ещё насыпать exactly once semantics, чтоб было совсем хорошо.

Вы говорите, что все вокруг, включая фреймворки, игнорируют эти вопросы. Вроде бы никто не игнорирует, просто вы не по тем ключевикам поискали и не нашли принятых в индустрии подходов.

Советую почитать таки про задачу двух генералов и основные выводы из неё, потому что всё остальное, включая существующие решения, отталкивается от неё.

Было бы приятно совсем-совсем новое изобрести, но всё что описано тут - основывается как раз на том что вы описали. Проблеме несколько тысяч лет, просто раньше гонцы были. Впрочем, айти это автоматизация реального мира, так или иначе.

Про фреймворки - я всё же вижу низкую популярность, во многих бекендах такое вообще не применяется. С другой стороны - не везде и финансы. Но если вспомнить что некоторое количество лет назад иногда в Яндекс Такси дважды вызывалась машина - это вот этот самый баг, и много лет был.

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

Тут на Хабре иногда дважды комменты публикуются. Я бекенд не смотрел, но что-то мне подсказывает что это вот оно самое о чем в статье.

Насколько я разобрался, проблема в общем случае нерешаема. И можно только в каждой конкретной ситуации применять свои решения, которые сработают в частном порядке.

Возможно поэтому нет одного общепринятого решения этой проблемы, и нет такого инструмента, который бы это решение предлагал.

И да, многим командам разработки дешевле не обращать на такие проблемы внимания и править неконсистентности вручную силами саппорта, чем устранять эти проблемы на уровне разработки. Потому что проблем этих много, как вы описали, и они сложные, их с наскока не решить. А убытков от них может быть не так и много.

Очень интересный опыт, если в вашем случае это очень важно для бизнеса. Это похоже на хайлоад, там проблемы похожей сложности. И тоже большинство не заморачивается за оптимизацию, пока может себе позволить.

Идемпотентность, увы, очень дорогое решение (слишком многое нужно хранить на стороне сервера), поэтому чаще стараются использовать другие решения или как-то оптимизировать использование ключей идемпотентности (там довольно много разных паттернов есть).
И именно из-за высокой стоимости прямо "из коробки" фреймворки такое не реализуют, хотя во многих готовых продуктах подобные инструменты есть (та же Kafka)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий