📬 Пост 52. Inbox Pattern - как не обработать одно событие дважды 😎
В прошлом посте разбирали Outbox: как надёжно отправлять события из нашего сервиса в Kafka. Но есть и обратная проблема 👀
Что делать, когда событие пришло в наш сервис, но Kafka доставила его повторно? Представим: Order Service отправил событие OrderPaid, а его получил Notification Service. Сервис должен отправить пользователю уведомление об оплате. Но после обработки Kafka может доставить то же сообщение ещё раз. Например, consumer обработал событие, но упал до фиксации своего offset. В итоге пользователь рискует получить два уведомления об одной оплате… 🔥 Здесь на помощь приходит Inbox Pattern
🧠 В чём суть? Мы сохраняем входящие события в отдельную таблицу inbox и отмечаем, какие из них уже обработаны. Например: CREATE TABLE inbox ( event_id UUID PRIMARY KEY, event_type VARCHAR(100), payload JSONB, processed BOOLEAN DEFAULT FALSE ); Ключевой момент - event_id уникален. Если одно событие прилетит повторно, база не даст сохранить его как новое.
⚙️ Как это работает на практике? Допустим, из Kafka пришло событие:
{ “eventId”: “abc-123”, “eventType”: “OrderCreated”, “orderId”: 500 }
Наш сервис действует так: Kafka ↓ получили событие ↓ сохранили в inbox ↓ проверили event_id ↓ обработали бизнес-логику ↓ зафиксировали результат При этом запись события в Inbox и изменение бизнес-данных нужно выполнять в одной транзакции БД. Например, если сервис начисляет бонусы за заказ, он в одной транзакции: Регистрирует event_id в Inbox. Начисляет бонусы. Фиксирует транзакцию. Если то же событие придёт снова, уникальный event_id покажет, что оно уже было обработано, и бонусы повторно не начислятся.
⚠️ Inbox и Outbox - в чём разница? Outbox Inbox Надёжно отправляем событие Надёжно принимаем событие Событие создаётся у нас Событие приходит извне БД → Kafka Kafka → наша БД Защита от потери публикации Защита от повторной обработки
💡 Что важно запомнить Inbox сам по себе не гарантирует, что сообщение физически придёт только один раз. Он позволяет безопасно переживать повторную доставку. И есть нюанс: если обработка включает внешний вызов, например отправку SMS, одна транзакция БД не защитит от повторного SMS. Для таких операций нужна отдельная идемпотентность или дополнительный механизм надёжной доставки. Поэтому в микросервисах нередко используют оба паттерна: Service A ↓ Outbox ↓ Kafka ↓ Inbox ↓ Service B Outbox помогает надёжно публиковать события, Inbox - не выполнять одну и ту же бизнес-операцию повторно. Вот такая связка 💪 #inbox #outbox #kafka #микросервисы #архитектура #системныйанализ