La double écriture est toujours un bug
Pourquoi écrire dans deux systèmes depuis le même code finit toujours par diverger, les seules sorties correctes, et comment choisir entre outbox et CDC selon ce que chacun vous fait payer.
Par Elias Varen7 min de lectureMembres
$this->orders->save($order); // PostgreSQL
$this->search->index($order); // moteur de recherche
$this->mailer->send($confirmation); // prestataire de mailCes trois lignes passent la revue de code tous les jours. Elles contiennent deux bugs, qui ne dépendent ni du langage, ni du framework, ni de l'ordre des lignes. Entre deux écritures vers deux systèmes qui ne partagent pas de transaction, il existe un instant où le processus peut mourir (déploiement, manque de mémoire, coupure réseau, timeout). À cet instant, un système sait et l'autre ignore. Rien ne viendra corriger l'écart, parce que le seul code qui connaissait l'intention vient de disparaître.
Je dis « toujours » et je le maintiens : la double écriture n'est pas un risque à évaluer, c'est une divergence dont seule la date est inconnue.
Pourquoi aucun aménagement ne la répare
Chaque tentative de sauver les trois lignes échoue pour une raison mécanique.
Changer l'ordre. Si l'on indexe avant d'enregistrer, la recherche montre une commande qui n'existe pas. Si l'on enregistre avant d'indexer, une commande existe et reste introuvable. Vous choisissez la forme de l'incohérence, jamais son absence.
Entourer d'un try/catch et compenser. La compensation est elle-même une écriture vers un autre système, qui peut échouer pour la même raison que la première. Surtout, le cas qui pose problème est la mort du processus, et dans ce cas aucun catch ne s'exécute.
Rejouer en mémoire. Le retry ne survit pas au processus qui le porte.
Mettre le second appel dans la transaction. L'appel externe réussit, puis le COMMIT échoue sur un conflit de sérialisation ou une connexion perdue. Le mail est parti pour une commande annulée. En prime, vous gardez une transaction et ses verrous ouverts pendant un appel réseau.
Le commit en deux phases. Il exige que les deux systèmes le supportent, ce que n'offrent ni une API HTTP, ni la plupart des brokers, ni un moteur de recherche ; et il fait de la disponibilité de l'un la condition des écritures de l'autre.
À lire ensuite
Toute la rubrique ArchitectureArchitecture
Lire sur un replica : les bugs de lecture périmée
Les trois anomalies qu'un replica de lecture introduit dans une application qui n'en avait pas, les quatre façons de lire ses propres écritures avec leur prix, et le critère pour décider quelles lectures ont le droit de quitter le primaire.
8 minMembres
Architecture
Lire un rapport de test de cohérence en trente minutes
Une méthode en cinq passes pour tirer une décision d'un rapport de test de cohérence sans être spécialiste des systèmes distribués : ce qui a été testé, ce qui était promis, ce qui a cassé, sous quelle panne, et ce que ça change pour votre usage.
8 minMembres
Architecture
Les horloges mentent : ce que ça casse dans votre code
Quatre mensonges de l'horloge (elle recule, elle diffère d'une machine à l'autre, elle dépend d'un fuseau, elle confond le fait et sa saisie) et pour chacun la parade, son prix et le cas où l'on peut s'en passer.
7 minMembres