Перейти к содержанию

Transactional Outbox vs Dual Write

Сравнения · Outbox/Inbox

Тип материала: инженерное сравнение; выводы зависят от требований и условий эксплуатации. Схемы и примеры иллюстративны.

INFERENCE: сравнивается обычная независимая запись business DB и broker без общей transaction. Термин dual write также встречается в migration protocols с дополнительными проверками; сам по себе он не описывает достаточную гарантию. Сценарии и схема — HYPOTHETICAL.

Failure matrix и критерии

Критерий Independent dual write Transactional Outbox
DB commit и delivery intent Два независимых действия Одна локальная DB transaction
Crash между ними Lost event либо ghost event Intent сохраняется вместе с business state
Broker outage API либо падает, либо оставляет gap Business commit возможен, outbox backlog растёт
Дубли Возможны при retries Ожидаемы у relay; consumer дедуплицирует
Ordering Не гарантируется порядком строк кода Также требует явного relay/key/version protocol
Эксплуатация Мало компонентов, сложный repair Relay, cleanup, lag и inbox/idempotency
Когда допустимо Разрешённая lossiness или отдельный доказанный protocol Нужна durable event intent вместе с commit

DB-first: payment committed, publish не состоялся — downstream ничего не знает. Publish-first: event ушёл, DB rollback — downstream верит в несуществующую операцию. Обработка exception не закрывает crash или потерянный response.

Выбранная граница

flowchart TB
    C["Command"] --> T["Одна DB transaction"]
    T --> B["Business rows"]
    T --> O["Outbox intent"]
    O --> R["Relay: publish и retry"]
    R --> K["Broker"]
    K --> I["Consumer: inbox + effect atomically"]

Outbox закрывает atomicity business+intent, но не объединяет producer и consumer databases. Consumer DB effect требует своего boundary. External HTTP effect требует provider idempotency либо иной protocol и reconciliation.

Конкретные сценарии

PaymentCaptured → reporting: потерянный intent нарушает полноту отчёта. Outbox с durable relay и consumer dedup оправдан. Суммы и IDs сверяются с ledger.

BetAccepted → history/risk: accepted bet должен иметь устойчивый integration event. Outbox можно читать CDC; consumer проверяет revision, чтобы stale update не перезаписал corrected bet.

Disposable telemetry: если бизнес явно допускает отдельные потери и определена observability, best-effort publish может быть осознанным выбором. Нельзя переносить его на financial postings.

Online migration: dual writes внутри контролируемой миграции требуют backfill, сравнения, rollback и знания transaction scope. FACT: Stripe описал staged online migration в 2017 году. Stripe. Это не доказательство безопасности любого произвольного DB+broker dual write.

Ограничения

Гарантия доставки условна: outbox не должен быть удалён до подтверждённого transfer, relay должен восстанавливаться, broker — сохранять input по заданному contract. «Intent durable» не равно «job успешно завершится».

Для transactional broker + совместимой external DB можно рассмотреть coordination/2PC, но это отдельная технология с recovery costs. Producer idempotence внутри broker не атомизирует запись в чужой DB.

FACT: Shopify объясняет staged job queue и повторную постановку после ошибки progress update. Shopify. Debezium предоставляет router, но приложение само создаёт atomic outbox. Debezium 3.3.

Типичные ошибки проектирования

Пометить sent до broker ack; считать inbox достаточным для payout API; выдавать transaction broker за SQL+broker atomicity; обещать delivery при бесконтрольном cleanup.

Вопросы на собеседовании

Почему retry publish недостаточен? После crash intent может не сохраниться, а успешный publish с потерянным ответом даст duplicate.

Зачем inbox при outbox? Relay at-least-once оставляет окно повторной доставки; producer atomic intent не решает consumer effect.

Практические упражнения

Нарисуйте два dual-write порядка и вставьте crash между действиями. Затем покажите outbox protocol и crash после broker accept. Ожидается, что потеря превращается в безопасно обнаруживаемый повтор, а не исчезают вообще все failure modes.

Дополнительные источники

Полная статья; Kafka 4.1 external systems.