Комментарии 6
Сообщения должны быть идемпотентны. Финансовые транзакции с кодом ФТ, статусами и пр.
Коммит в продюсере должен быть после получения ответа от очереди, что сообщение принято.
2 А если коммит не пройдет?
Если транзакция откатится в момент, когда kafka вернет ок - будет ненужное сообщение. Но, повторяю (и автор это указывает), система должна обеспечивать идемпотентность. Сообщения должны говорить не "сделай то-то", "добавь сто рублей на счет", а "приведи систему в такое-то состояние", "укажи, что количество товара сейчас, на столько-то часов, столько минут, секунд, такое-то".
Здесь https://www.litres.ru/book/gven-shapira/apache-kafka-potokovaya-obrabotka-i-analiz-dannyh-pdf-epub-42225434/ все хорошо рассмотрено.
Таблица outbox и нужна, чтобы присвоить идентификатор для транзакции (проводке) и дальше идемпотентность будет опираться на этот идентификатор.
Не указал про консьюмера, но там тоже проблема решается идемпотентностью, и тем, что консьюмер отправляет сигнал кафке о том, что сообщение потреблено (сдвинуть указатель) только после фиксации в своей базе.
Что-то я не понял пользы на вашем примере:
Наивный вариант:
Наивным решением было бы "сначала запишем в БД, а потом отправим в брокер", однако такое решение ломается при сбоях, если процесс упадет между этими двумя шагами, то мы либо потеряем событие, либо отправим дубль.
Вариант с очередью:
Удалять обработанные события: меньше места в БД, но теряется история.
Хранить и помечать статусом (например,
SENT): есть аудит, но таблица будет расти и ее нужно архивировать/очищать.
Transactional Outbox гарантирует доставку at-least-once (хотя бы один раз).
Т.е. в обоих вариантах отправим дубль. И если в первый вариант добавить в БД флаг SENT
и делать повторы до успеха, то будет та же доставка at-least-once, но без внешних очередей.
Вообще смысл этого паттерна не в том, чтобы избежать дублей (все таки это at-least-once паттерн), а в том, чтобы не потерять событие и не получить рассинхрон при сбое. В данном случае разница между "наивным решением" и описанном в статье в атомарности записи и контролируемом ретрае.
Например, если сервис упал после записи в БД и до публикации - событие будет потеряно навсегда. Если упал после публикации и до коммита в БД, то можем получить фантомное событие, т.е. из брокера оно пришло, а в БД его нет.
По поводу outbox таблицы без брокера - это тоже валидный вариант, в данной статье приведен лишь один из вариантов реализации, в котором используется асинхронная обработка. Такой вариант нужен, когда событие может быть обработано не сразу. Подход с брокером может помочь, когда нам событие нужно обрабатывать сразу в нескольких сервисов, которые будут читать этот топик.

Паттерн Transactional Outbox — обеспечиваем консистентность между микросервисами на примере Java