Slovník pojmů
Outbox Pattern
Transactional Outbox Pattern nahrazuje nespolehlivý dvojitý zápis jedním atomickým databázovým krokem a opakovatelným doručením. Neslibuje však exactly-once delivery.
Stručná definice
Business změna a záměr odeslat zprávu vzniknou společně.
Přesnější označení transakční varianty je Transactional Outbox Pattern. Aplikace uloží změnu business dat i zprávu určenou k pozdějšímu odeslání do stejného úložiště v jedné databázové transakci. Pokud transakce neprojde, nevznikne ani objednávka, ani zpráva. Pokud se potvrdí, zpráva zůstane trvale připravená k doručení i po pádu aplikačního procesu.
Samostatný publisher nebo relay načte nezpracované položky a publikuje je do message brokeru. Potvrzení databáze a potvrzení brokeru ale stále nejsou jednou atomickou operací: relay může po publikování spadnout dřív, než uloží stav „odesláno“. Proto je běžné doručení alespoň jednou a příjemce musí bezpečně zvládnout duplicitu.
Jaký problém řeší
Dual write může skončit dvěma různými pravdami.
Přímý zápis do databáze a samostatné odeslání do brokeru mají mezi sebou okamžik, ve kterém může aplikace, síť nebo broker selhat.
- objednávka se uloží, ale OrderCreated se kvůli pádu procesu nikdy neodešle
- zpráva se odešle před commitem, ale databázová transakce se následně vrátí
- HTTP request skončí timeoutem, přestože jedna z operací už proběhla
- publisher neví, zda broker zprávu přijal, a musí pokus bezpečně zopakovat
- více instancí publisheru soutěží o stejné outbox položky a musí koordinovat jejich zpracování
Praktický příklad a diagram
Nová objednávka a událost OrderCreated
PHP aplikace přijme objednávku a otevře krátkou transakci. Do tabulky orders vloží objednávku a do tabulky outbox vloží záznam s jedinečným message_id, typem OrderCreated, identifikátorem objednávky, payloadem, časem vzniku a případně pořadím pro daný aggregate. Teprve potom provede commit. Request nemusí čekat na RabbitMQ a síťové volání nezůstává uvnitř databázové transakce.
RabbitMQ může být cílovým brokerem, není však součástí definice vzoru. Worker pravidelně vybírá neodeslané položky, publikuje je a po potvrzení brokerem eviduje zpracování. Když spadne mezi posledními dvěma kroky, stejný message_id se může objevit znovu; consumer proto před vedlejším účinkem ověří, zda tuto zprávu už nezpracoval.
Textový diagram a jeho alternativa
HTTP request
↓
DB TRANSACTION
├─ Order(order-42)
└─ Outbox(message-123)
↓ COMMIT
Publisher / Relay
↓
Message broker
↓ may deliver again
Idempotent consumer
Jak funguje
Od lokální transakce k doručení zprávy
Sekvence je zároveň textovou alternativou diagramu. Implementace může používat polling nebo change data capture, základní hranice spolehlivosti zůstává stejná.
- Jedna lokální transakce Aplikace vloží nebo změní business data a současně vytvoří odolný outbox záznam. Obě změny se potvrdí, nebo se obě vrátí.
- Výběr čekajících položek Publisher načte dávku nezpracovaných zpráv. Při více workerech potřebuje rozumné zamykání, leasing nebo jiný způsob, který omezuje zbytečnou paralelní práci.
- Publikování Fronta zpráv nebo topic přijme zprávu. Relay rozlišuje přechodnou chybu od trvalé chyby a používá omezený retry s backoffem.
- Evidence výsledku Po potvrzení brokerem publisher položku označí jako publikovanou, přesune ji nebo drží samostatný cursor. Staré záznamy se uklízejí podle retenční politiky.
- Idempotentní zpracování Idempotence chrání business účinek před opakovaným message_id. Deduplikace musí být atomická s lokální změnou consumera, jinak zůstane nová mezera dual write.
Hlavní části a principy
Outbox je provozní mechanismus, ne jen pomocná tabulka.
Spolehlivost vzniká až kombinací atomického zápisu, relaye, pozorovatelného retry a bezpečného příjemce.
Outbox záznam
Nese stabilní identifikátor zprávy, typ a verzi kontraktu, čas vzniku, payload a identitu zdroje. U relační databáze jde často o tabulku; v dokumentovém úložišti může být zpráva součástí dokumentu nebo transakční dávky.
Polling publisher
Worker v intervalech čte čekající položky. Je snadno pochopitelný, ale potřebuje vhodný index, dávkování, omezení souběhu a metriku stáří nejstarší zprávy. Příliš agresivní polling zatěžuje databázi, příliš pomalý zvyšuje latenci.
CDC nebo log tailing
Change Data Capture může sledovat databázový log a změny předávat s nižší latencí. Je to pouze varianta relaye: přidává závislost na konkrétní databázi a provozní infrastruktuře, takže se nehodí automaticky všude.
Pořadí zpráv
Globální pořadí bývá drahé a často zbytečné. Důležité může být pořadí událostí jedné objednávky; pak se ukládá aggregate_id a sekvence a odpovídajícím způsobem se partitionuje broker. Samotný outbox pořadí u všech consumerů nezaručí.
Doménová a integrační zpráva
Interní doménová událost nemusí být přímo veřejným integračním kontraktem. Outbox payload má zveřejnit stabilní minimum pro konzumenty a nesmí bez rozmyslu kopírovat interní model aplikace.
Výhody a omezení
Méně ztracených zpráv za cenu dalšího uloženého stavu.
Přínosy
- odstraňuje mezeru mezi commitem business dat a trvalým zaznamenáním záměru publikovat
- nevyžaduje distribuovanou 2PC transakci mezi databází a brokerem
- umožňuje obnovit publikování po pádu procesu nebo dočasné nedostupnosti brokeru
- zpřístupňuje backlog, stáří a chyby doručení pro monitoring a řízený zásah
Omezení a časté chyby
- Outbox Pattern nezajišťuje exactly-once delivery; relay může publikovat stejnou zprávu vícekrát
- consumer bez idempotence může podruhé odeslat e-mail, odečíst sklad nebo vytvořit platbu
- stav „published“ uložený před potvrzením brokeru může zprávu ztratit, uložený po něm připouští duplicitu
- chybějící retence, index a alert na stáří backlogu způsobí růst tabulky a skryté zpoždění
- outbox sám nekoordinuje několik business kroků a neřeší všechny distribuované transakce
Kdy dává smysl
Když potvrzená lokální změna musí spolehlivě vyvolat navazující práci.
Vzor se hodí pro události objednávek, aktualizaci vyhledávacího indexu, synchronizaci se skladem nebo zahájení asynchronního workflow. Uplatní se v monolitu i v mikroservisách; podmínkou není CQRS, Event Sourcing ani konkrétní broker. Rozhodující je dvojice změny v databázi a zprávy, které spolu významově musejí vzniknout.
Nedává velký přínos, pokud navazující operace může být bezpečně odvozena přímo z autoritativních dat a občasné spuštění plánovače stačí. Outbox také není náhradou za Saga Pattern: pomáhá spolehlivě přenést jednotlivé příkazy a události, ale stav celého procesu, kompenzace a rozhodnutí o dalším kroku musí navrhnout workflow zvlášť.
Provoz a kontrola
Sledujte doručení jako samostatný produkční proces.
Úspěšný commit znamená uložený záměr, ne dokončené zpracování ve všech navazujících systémech.
- měřit počet čekajících položek, jejich stáří, dobu doručení a počet opakování
- používat stabilní message_id a atomickou deduplikaci na straně consumera
- definovat limit retry, backoff, dead-letter nebo dohledatelný stav trvalé chyby
- testovat pád po commitu databáze, po publishi brokeru i během označení zpracování
- verzovat kontrakt zprávy a chránit pořadí jen v rozsahu, kde má business význam
- udržovat retenci a indexy outboxu tak, aby publisher nezpomaloval běžné transakce
Časté otázky
Garance Transactional Outbox Patternu
Zaručuje Outbox Pattern exactly-once delivery?
Ne. Relay může po úspěšném publishi spadnout před uložením výsledku a zprávu po restartu poslat znovu. Běžným cílem je at-least-once doručení a idempotentní zpracování.
Musí být outbox vždy samostatná SQL tabulka?
Nemusí. U relační databáze je tabulka běžná, dokumentová databáze může použít dokument nebo change feed. Podstatné je, aby business změna a zpráva vznikly v jedné podporované atomické transakční hranici.
Je pro Outbox Pattern nutný RabbitMQ?
Ne. Cílem může být RabbitMQ, Kafka, cloudová fronta nebo jiný doručovací systém. Vzor řeší spojení lokální databázové změny s pozdějším publikováním, nikoli výběr brokeru.
Vyřeší outbox celou distribuovanou transakci?
Ne. Spolehlivě zaznamená a předá zprávu navázanou na lokální commit. Následné business kroky, jejich stav, konflikty a kompenzace vyžadují další návrh, například ságu.
Lze místo pollingu použít CDC?
Ano, pokud databáze a provozní prostředí poskytují vhodný change feed nebo log. CDC může snížit latenci, ale nemění požadavek na idempotenci, monitoring ani správu schématu zpráv.
Jak řeším integrační spolehlivost
Změnu dat odděluji od síťového doručení jasnou a pozorovatelnou hranicí.
Při návrhu integračních toků počítám s pády mezi kroky, duplicitami, retry i dohledáním stavu zprávy v produkci.