Поделюсь изящным решением с прода. Шорткат проблемы eventual consistency + low latency.
Цель: курьер завершил заказ — хотим сразу показать экран: «Поднажми, ещё 2 доставки и $20 твои» или «Готово, жди бабло на карту».
Проблема: зачёт заказа асинхронный: условия кампании, антифрод, манипуляции с бонусами. В час пик — далеко не мгновенно. Когда курьер смотрит на экран, его заказ ещё нигде не учтён.
🤡 Взять прогресс и сделать +1 на клиенте. Воркер уже успел — получаем +2. Счётчик скачет, курьер пишет в саппорт.
🤡 Выполнить весь флоу синхронно в ручке. Оборвалась связь на середине — partial state и ручные фиксы в проде.
Решение
Тот же самый код, но с флагом dry-run.
Ручка зовёт боевой процесс зачёта с order_id: заказ ещё не учтён? подходит под цель? сколько будет после? — и возвращает результат. Просто ничего никуда не пишет.
POST /v1/goals/progress { "dry_run_order_id": "o-88231"}
200 OK { "campaign_id": "weekend-20", "order_applied": true, "progress": 8, "target": 10, "reward": {"amount": 20, "currency": "USD"} }
✅ Один код-путь. Прогноз не разойдётся с реальностью, это буквально та же логика. Не надо писать второй калькулятор, который через полгода начнёт врать.
✅ Идемпотентность по order_id закрывает гонку. Воркер успел? dry-run это видит.
✅ Ручку можно ретраить, дёргать сколько угодно, звать из тестов.
⚠️ Логику надо разложить так: сначала весь расчёт, сайдэффекты строго после.
Тогда dry-run — просто ранний return перед ними. Один сайдэффект, уехавший выше точки выхода, и курьеру улетел пуш про бонус, которого нет.
⚠️ Это прогноз, а не гарантия. Антифрод зарубит позже. Поэтому в UI «жди бабло на карту», а не «$20 зачислено».