TGViewer
Art of Code Art of Code @codeof_art · 2.18K subscribers
Post #349 309
System Design: backend

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

Выглядит он так: сначала коммитим платёж в базу, потом отправляем событие в брокер. Проблема в том, что это две операции в двух разных системах, и между ними может случиться что угодно. Процесс упадёт после коммита или сеть пропадёт на полсекунды, и в итоге деньги у клиента списаны, а склад про заказ так и не узнал. Если поменять порядок и сначала отправить событие, получится зеркальная беда, потому что транзакция в базе может откатиться уже после того, как склад начал собирать заказ, за который никто не заплатил. Хороший кандидат на этом месте останавливается и спрашивает интервьюера, что для бизнеса хуже, потерять событие или получить его два раза. Ответ почти всегда одинаковый. Потерянный заказ обходится дороже, потому что о нём никто не узнает, пока клиент не придёт ругаться в поддержку, а лишнюю копию события получатель может просто отбросить. Раз так, задача сводится к тому, чтобы гарантировать доставку хотя бы один раз и научить получателя спокойно относиться к повторам.

Для этого используют приём, который называется Transactional Outbox. В той же базе, где лежат платежи, заводится отдельная таблица для исходящих событий, и строка в неё пишется в той же транзакции, что и сам платёж. База умеет гарантировать атомарность внутри себя, поэтому после коммита у нас либо есть и платёж, и событие о нём, либо нет ни того, ни другого. В Kafka эти строки перекладывает отдельный процесс, его обычно называют реле. Он берёт из таблицы то, что ещё не отправлено, публикует в брокер и только после подтверждения помечает строку как отправленную. Если реле упадёт между публикацией и пометкой, после перезапуска оно отправит те же строки повторно, и здесь становится понятно, зачем вопрос про дубли задавали в самом начале. Когда экземпляров реле несколько, строки удобно разбирать через SELECT FOR UPDATE SKIP LOCKED, чтобы два процесса не схватили одну запись, а ключом сообщения лучше делать id заказа, тогда все события одного заказа попадут в одну партицию и склад получит их в правильном порядке.

Раз повторы стали нормальной частью системы, получатель обязан с ними справляться. Склад хранит идентификаторы уже обработанных событий и просто пропускает те, что видел раньше. Если про это забыть, Outbox решит проблему у отправителя и тут же создаст новую у склада, где один и тот же заказ начнут собирать дважды. Обычно после этого интервьюер переходит к эксплуатации. Опрашивать таблицу раз в секунду можно, но это нагружает базу и добавляет задержку, поэтому в больших системах изменения часто читают прямо из журнала транзакций с помощью CDC вроде Debezium. Отправленные строки со временем нужно чистить, иначе таблица событий разрастётся быстрее самих платежей. Для мониторинга хватает одной метрики, возраста самой старой неотправленной строки. Если он начал расти, значит реле встало или брокер лежит, и лучше узнать об этом от алерта, чем от склада.

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

Подписаться: @codeof_art
Вступить в чатик: @code_of_art
  • ❤ 2
  • 👍 1
More from @codeof_art
  1. Sep 29, 2026Ozon: Полный слив направления Go-разработки Продолжаем грабить бигтехи, чтобы вы реально п…
  2. Sep 27, 2026Осень обычно время, когда компании запускают свои образовательные потоки, и в этом году мн…
  3. Sep 27, 2026Ты поступишь в ШАД Старт набора на наши ШАДовские курсы: без воды и лишней теории, 3 месяц…
  4. Sep 22, 2026Новый пост из серии про паттерны на реальном коде. Разбираем штуку, с которой рано или поз…
  5. Sep 21, 2026Полный цикл собесов в Яндекс (Бэкенд 2026) Сейчас студенты наших курсов под чутким сопрово…
  6. Sep 20, 2026❗️ Яндекс открыл Intern Week Offer на стажировку, где всего за неделю ты можешь получить о…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →