Le patron Outbox : fiabiliser les événements
AvancéProblème : après avoir enregistré une commande, il faut publier un événement (Kafka, RabbitMQ). Si l'application plante entre les deux, la commande existe sans événement (ou l'inverse). Il n'existe pas de transaction commune entre la base et le broker.
La solution : écrire l'événement dans la même transaction#
BEGIN;
INSERT INTO commandes (client_id, total) VALUES (42, 99.90) RETURNING id;
INSERT INTO outbox (type, cle, charge_utile)
VALUES ('commande.creee', '1234', '{"id": 1234, "total": 99.90}');
COMMIT;
Un processus séparé lit régulièrement la table outbox, publie les messages au broker, puis les marque comme publiés :
SELECT id, type, cle, charge_utile
FROM outbox
WHERE publie_le IS NULL
ORDER BY id
LIMIT 100
FOR UPDATE SKIP LOCKED; -- plusieurs relais possibles, sans double traitement
Propriétés et limites#
- Au moins une fois : le relais peut publier un message puis planter avant de le marquer ; les consommateurs doivent donc être idempotents (voir ci-dessous).
- L'ordre est conservé par clé si l'on publie dans l'ordre d'
idpar clé. - Alternative : capturer les changements du journal WAL avec la réplication logique (Debezium) : plus complexe, mais sans table
outboxinterrogée.
Cas pratique : le patron outbox est la réponse standard à la question d'entretien « comment garantir qu'on n'enregistre pas une commande sans publier l'événement (ni l'inverse) sans transaction distribuée ? ».