Aller au contenu
Tous les articles
2 min de lecture

Exactly-once n'est pas un mythe, mais ce n'est pas ce que vous croyez

Systèmes distribuésPythonPostgreSQL

On répète souvent qu'« exactly-once est impossible ». C'est vrai pour la livraison : dès qu'un réseau peut perdre des paquets, un émetteur ne peut jamais savoir avec certitude si son message est arrivé ou si c'est l'accusé de réception qui s'est perdu. Il doit choisir entre réessayer (au moins une fois) ou abandonner (au plus une fois).

Mais ce n'est pas la garantie qui nous intéresse en pratique.

Ce que l'on veut vraiment

Ce qui compte, c'est que l'effet d'un message ne se produise qu'une fois. Débiter un compte deux fois est un bug ; recevoir deux fois le message qui demande le débit n'en est pas un, tant que le second est ignoré.

La recette tient en deux ingrédients :

  • une clé d'idempotence stable, dérivée du message et non de la tentative ;
  • un enregistrement atomique du résultat et de la clé, dans la même transaction.

En pratique

BEGIN;

INSERT INTO processed_messages (idempotency_key)
VALUES ($1)
ON CONFLICT DO NOTHING;

-- Si aucune ligne n'a été insérée, le message a déjà été traité.
-- On sort sans rejouer l'effet métier.

UPDATE accounts
SET balance = balance - $2
WHERE id = $3;

COMMIT;

Le point clé est que l'écriture de la clé et l'effet métier partagent la même transaction. Si l'un échoue, l'autre est annulé. Le worker peut alors crasher n'importe quand : au redémarrage, le message sera rejoué et la contrainte d'unicité fera le tri.

Le piège du cache

La tentation est grande de mettre le registre des clés dans Redis, « pour aller plus vite ». C'est exactement là que la garantie se casse : Redis et PostgreSQL ne partagent pas de transaction. Entre le SET dans Redis et le COMMIT dans PostgreSQL, il existe une fenêtre où un crash laisse le système dans un état incohérent.

Si le registre doit vivre ailleurs que dans la base métier, il faut alors accepter un mécanisme de réconciliation , et le concevoir explicitement, pas le découvrir en production.

Ce qu'il faut retenir

Exactly-once n'est pas une case à cocher dans la configuration du broker. C'est une propriété que l'on construit côté consommateur, en rendant le traitement idempotent et en le rendant atomique avec l'enregistrement de sa propre trace.