Forge Labs
Toutes les publications

Automatisation

Concevoir une file de reprise pour les erreurs d'automatisation

Une architecture concrète pour reprendre les erreurs d’automatisation sans bloquer le flux ni dupliquer les effets : retry, idempotence, quarantaine et contrôle humain.

16 août 202614 minRedaction
Illustration de l’article : Concevoir une file de reprise pour les erreurs d'automatisation

Une file de reprise ne doit pas être ajoutée après le premier incident sérieux. Elle fait partie du produit au même titre que le workflow principal. Son rôle est simple à énoncer : lorsqu’une étape échoue, le système doit conserver un élément identifiable, décider si une nouvelle tentative est raisonnable, empêcher les effets en double et donner à un opérateur les informations nécessaires pour résoudre le cas.

Atelier de travail autour de Concevoir une file de reprise pour les erreurs d'automatisation
Concevoir une file de reprise pour les erreurs d'automatisation — observer des cas réels avant de choisir ce qui sera automatisé.

L’architecture minimale comprend cinq composants : une file principale, un consommateur, une politique de retry, une file de quarantaine ou dead-letter queue, et un registre de traitement consultable. Il faut y ajouter une règle souvent oubliée : toute action produisant un effet métier doit être idempotente, ou protégée par un mécanisme équivalent.

  1. Un événement portant un identifiant stable entre dans la file principale.
  2. Un consommateur réserve l’événement et tente l’étape attendue.
  3. En cas d’erreur transitoire, une nouvelle tentative est planifiée avec un délai croissant.
  4. En cas d’erreur permanente, ambiguë ou trop répétée, l’élément passe en quarantaine.
  5. Un opérateur diagnostique, corrige, rejoue ou clôt le cas avec une preuve de résolution.

Ce circuit évite qu’un message défectueux bloque toute la chaîne, mais il ne garantit pas à lui seul qu’aucun événement ne sera perdu ou dupliqué. La fiabilité dépend aussi de la durée de rétention, des droits d’accès, du stockage du registre, des acquittements et de l’idempotence du traitement.

Le cycle de vie d’une erreur

Une erreur exploitable n’est pas seulement une trace technique. C’est un dossier doté d’un identifiant, d’un état et d’un responsable. Son cycle de vie commence avant l’échec : l’événement initial doit recevoir un identifiant stable, conservé lors de chaque redélivrance et de chaque reprise manuelle.

Lorsqu’un consommateur reçoit l’événement, il enregistre le début de la tentative. Si l’opération réussit, il conserve la preuve de l’effet obtenu avant d’acquitter le message. Si elle échoue, il classe l’erreur :

  • transitoire : indisponibilité brève, limitation de débit, délai dépassé ou coupure réseau ;
  • permanente : donnée invalide, destinataire inconnu, règle métier violée ou ressource supprimée ;
  • ambiguë : le système distant a peut-être effectué l’action, mais la réponse n’est pas parvenue ;
  • critique : la répétition pourrait créer un paiement, une expédition, une écriture comptable ou une notification indue.

Cette classification détermine le chemin suivant. Une erreur transitoire peut être retentée. Une erreur permanente va généralement en quarantaine. Une erreur ambiguë impose d’abord une vérification de l’état externe. Une erreur critique peut exiger une validation humaine dès la première occurrence.

Dans Amazon SQS, un message reçu devient temporairement invisible. S’il n’est pas supprimé avant l’expiration du délai de visibilité, il peut redevenir disponible et être livré de nouveau. Ce comportement aide à reprendre un traitement interrompu, mais oblige le consommateur à tolérer une redélivrance. [3]

Retry et backoff : retenter seulement ce qui peut réussir

Un retry est une nouvelle tentative du même traitement. Il est pertinent lorsque la cause peut disparaître sans modification de la donnée : service momentanément indisponible, quota temporaire ou incident réseau. Il est inutile de renvoyer dix fois une adresse email syntaxiquement invalide.

Le délai entre les tentatives doit généralement augmenter. Un backoff exponentiel peut, par exemple, attendre 10 secondes, puis 30 secondes, 90 secondes et 270 secondes. Un léger aléa, ou jitter, évite que des centaines de traitements repartent au même instant. Azure distingue explicitement les fautes transitoires, adaptées au retry, des erreurs qui nécessitent une autre réponse. [4] Google Cloud Pub/Sub permet également de configurer une politique de retry avec backoff exponentiel au niveau d’une souscription. [6]

Cette politique concerne les échecs techniques pour lesquels aucun effet externe n’a été produit, ou dont l’échec explicite est connu. Si un appel a pu réussir avant une coupure ou un délai dépassé, le système quitte le retry technique et passe en réconciliation : il interroge le fournisseur à partir de la clé d’idempotence ou d’une référence métier. Une réussite retrouvée est enregistrée sans nouvel appel ; une absence confirmée autorise une nouvelle tentative avec la même clé ; une issue qui reste inconnue va en revue manuelle. Aucun rejeu aveugle ne doit suivre un résultat externe ambigu.

Trois limites sont indispensables :

  • un nombre maximal de tentatives ;
  • une durée maximale depuis la première erreur ;
  • un budget global de retry pour ne pas saturer le service dépendant.

Sans ces limites, les consommateurs peuvent entretenir une retry storm : les nouvelles tentatives augmentent la charge sur un service déjà dégradé et retardent son rétablissement. [5] Le bon seuil n’est donc pas universel. Trois tentatives peuvent être excessives pour un paiement ambigu et insuffisantes pour une synchronisation non urgente. Il faut partir de la criticité métier, des délais acceptables et des caractéristiques du fournisseur, puis tester.

L’idempotence empêche la reprise de produire un doublon

Un traitement idempotent peut être exécuté plusieurs fois sans produire plusieurs fois son effet métier. Cela ne signifie pas que le code ne s’exécute qu’une fois. Cela signifie qu’un second passage reconnaît l’effet déjà obtenu et retourne le même résultat utile au lieu de le recréer.

La protection repose sur une clé d’idempotence stable et persistée, par exemple confirmation-commande:CMD-8472. Avant tout effet externe, le consommateur doit acquérir cette clé de façon atomique : soit par une insertion soumise à une contrainte d’unicité, soit par une comparaison-échange (compare-and-set) depuis un état qui autorise la reprise. Une lecture suivie d’une écriture ne suffit pas, car deux consommateurs pourraient tous deux lire « absent » puis exécuter l’effet.

  • si l’acquisition réussit, le registre passe à IN_PROGRESS — l’état PROCESSING de la machine ci-dessous — avec l’identifiant du propriétaire et l’expiration de son lease ;
  • si la clé existe avec l’état SUCCEEDED, le système restitue le résultat et sa référence sans réexécuter l’effet ;
  • si la clé est déjà IN_PROGRESS, aucun second consommateur n’exécute l’effet ; après expiration du lease, un nouveau propriétaire ne peut l’acquérir que par une comparaison-échange atomique ;
  • si l’issue de l’appel externe reste inconnue, le dossier passe en réconciliation ou en revue manuelle, jamais directement en rejeu.

L’expiration du lease prouve seulement que le propriétaire n’est plus actif ; elle ne prouve pas que l’effet externe n’a pas eu lieu. Lorsque l’API distante accepte une clé d’idempotence, la même clé doit être réutilisée à chaque tentative. Une référence métier unique et consultable permet aussi de sonder l’état distant après un délai dépassé.

Pour découpler l’écriture locale de l’appel externe, l’intention peut être placée dans une outbox durable au cours de la même transaction que le registre. L’entrée porte la clé d’idempotence, une référence métier et son état. Un worker acquiert cette entrée, appelle le fournisseur, puis persiste la référence retournée et SUCCEEDED. S’il tombe après un succès externe mais avant cette dernière écriture, la reprise sonde le fournisseur : elle complète le registre si le succès est retrouvé, retente avec la même clé si l’absence est confirmée, et demande une revue manuelle si l’issue demeure indéterminable.

Une transaction locale ne couvre pas automatiquement une API externe. Cette architecture réduit les doublons par acquisition atomique, déduplication et réconciliation, mais ne constitue pas une promesse générale d’exactly-once. Sans clé acceptée ni référence consultable côté fournisseur, une issue ambiguë doit rester bloquée jusqu’à une décision humaine plutôt que provoquer un nouvel effet à l’aveugle.

Dead-letter queue et quarantaine ne sont pas exactement la même chose

Une dead-letter queue, ou DLQ, isole les messages qui n’ont pas pu être consommés dans les conditions prévues. Amazon SQS permet de définir un nombre maximal de réceptions avant le transfert vers cette file. Elle protège le débit de la file principale et facilite l’analyse des cas récalcitrants.

La quarantaine est une notion plus large et plus métier. Elle peut s’appuyer sur une DLQ, mais elle ajoute un registre, des motifs normalisés, un propriétaire, des droits d’action et une interface de résolution. Une DLQ sans surveillance devient rapidement un cimetière invisible. Une quarantaine conçue comme une fonction du produit répond au contraire à cinq questions :

  • Pourquoi cet élément est-il ici ?
  • Quel effet a déjà été produit ?
  • Qui doit décider de la suite ?
  • Quelle action est autorisée : corriger, rejouer, ignorer ou compenser ?
  • Quelle preuve permettra de clôturer le cas ?

Le basculement peut intervenir après le nombre maximal de tentatives, dès qu’une erreur est classée permanente, lorsque l’âge du dossier dépasse un seuil ou lorsque le risque métier interdit le retry automatique.

Un modèle d’enregistrement assez simple pour être exploité

Le registre peut commencer par une table relationnelle. L’important n’est pas le choix du stockage, mais la présence de champs stables et interrogeables.

{
  "error_id": "err_01J...",
  "event_id": "evt_8472",
  "workflow": "confirmation_commande",
  "step": "creation_expedition",
  "idempotency_key": "expedition:CMD-8472",
  "state": "QUARANTINED",
  "reason_code": "INVALID_POSTAL_ADDRESS",
  "error_summary": "Code postal incompatible avec le pays",
  "attempt_count": 3,
  "first_failed_at": "2026-08-11T08:14:00Z",
  "last_failed_at": "2026-08-11T08:19:00Z",
  "next_attempt_at": null,
  "payload_reference": "obj://errors/evt_8472",
  "external_reference": null,
  "operator_id": null,
  "resolution_action": null,
  "resolution_evidence": null,
  "resolved_at": null
}

Évitez de copier sans nécessité le contenu complet du message dans le journal. Une référence vers une charge utile chiffrée et soumise à une durée de conservation est souvent préférable. Le résumé doit aider au diagnostic sans exposer de secret, de jeton d’accès ou de donnée personnelle superflue.

Une machine à états lisible

ÉtatSignificationTransitions autorisées
PENDINGÉvénement prêt à être traitéPROCESSING
PROCESSINGTentative en coursSUCCEEDED, RETRY_SCHEDULED, QUARANTINED
RETRY_SCHEDULEDErreur transitoire, nouvelle tentative planifiéePROCESSING, QUARANTINED
QUARANTINEDAutomatisme arrêté, diagnostic requisREADY_FOR_REPLAY, IGNORED, COMPENSATED
READY_FOR_REPLAYCorrection validée et reprise autoriséePROCESSING
SUCCEEDEDEffet obtenu et vérifiéÉtat terminal
IGNOREDCas clôturé sans exécution, avec justificationÉtat terminal
COMPENSATEDEffet antérieur annulé ou neutraliséÉtat terminal

Chaque transition doit être atomique dans le registre, datée et attribuée à un service ou à un opérateur. Le bouton « rejouer » ne doit pas modifier directement l’état en SUCCEEDED : il autorise une nouvelle tentative, laquelle doit encore vérifier l’idempotence et produire sa propre preuve.

Exemple : reprendre une confirmation sans la dupliquer

Une commande est payée, puis un workflow doit créer l’expédition et envoyer la confirmation. L’appel au transporteur expire après huit secondes. Le système ne sait pas si l’expédition a été créée.

Une reprise sûre ne rappelle pas immédiatement l’API de création. Elle cherche d’abord une expédition portant la référence unique CMD-8472. Si elle existe, le workflow enregistre son numéro et marque l’étape comme réussie. Si son absence est confirmée, il acquiert atomiquement l’étape puis rappelle l’API avec la même clé d’idempotence. Si la recherche reste inconclusive, l’étape va en revue manuelle : elle n’est pas rejouée à l’aveugle.

La confirmation suit sa propre outbox durable, créée dans la transaction locale avec la clé confirmation-email:CMD-8472. Le worker acquiert l’entrée, transmet cette clé au fournisseur si son API la prend en charge et conserve aussi un identifiant de message ou une référence consultable. Après un crash survenu avant l’écriture de SUCCEEDED, la reprise sonde le fournisseur : un envoi retrouvé complète le registre sans renvoi ; une absence explicite autorise une nouvelle tentative avec la même clé ; une issue inconnue déclenche une revue manuelle. Si le fournisseur ne permet ni déduplication ni consultation, le système ne peut pas garantir qu’un nouvel envoi serait sans doublon.

Le message peut donc être livré plusieurs fois sans supposer une exécution exactly-once. Le risque de doublon est borné par l’acquisition atomique, les références stables, la déduplication du fournisseur lorsqu’elle existe et la réconciliation avant toute nouvelle émission.

Exemple : quand la reprise automatique doit s’arrêter

Un workflow prépare le remboursement d’une facture, mais le montant reçu diffère du solde comptable et une note indique un litige client. Retenter l’opération ne corrigera pas cette contradiction. Pire, un retry automatique pourrait déclencher un remboursement indu.

Le dossier doit passer immédiatement en quarantaine avec le motif REFUND_AMOUNT_CONFLICT. Un opérateur habilité compare la facture, les paiements et le litige, puis choisit entre correction, rejet ou remboursement validé. La reprise exige une double validation si le montant dépasse le seuil défini. La preuve de résolution comprend la décision, l’identité des validateurs et la référence du remboursement éventuel.

Le critère d’arrêt n’est donc pas seulement technique. Une incertitude irréversible, réglementée ou financièrement sensible justifie une intervention humaine, parfois dès la première erreur.

Observabilité et sécurité

Le tableau de bord doit distinguer la santé du workflow de la simple disponibilité des serveurs. Suivez au minimum :

  • le nombre d’erreurs par workflow, étape et motif ;
  • le taux de succès après retry ;
  • l’âge du plus ancien dossier non résolu ;
  • la profondeur de la quarantaine ;
  • le nombre moyen de tentatives ;
  • le délai médian et maximal de résolution ;
  • les reprises manuelles échouées et les doublons détectés.

Les alertes doivent porter sur une tendance ou un risque : hausse soudaine d’un motif, quarantaine vieillissante, absence de consommation ou dépassement d’un délai métier. Une alerte pour chaque message crée du bruit et masque les incidents structurants.

Côté sécurité, séparez les droits de lecture, de correction, de rejeu et de clôture. Une personne autorisée à consulter une erreur ne doit pas nécessairement pouvoir relancer un paiement. Journalisez les actions d’administration, protégez les charges utiles sensibles, retirez les secrets des traces et fixez une durée de conservation. Le guide ANSSI sur l’architecture d’un système de journalisation [7] décrit les journaux comme une base utile à la détection et à l’analyse d’incidents de sécurité. L’ANSSI définit par ailleurs la supervision de sécurité [9] comme l’ensemble des moyens permettant de détecter, qualifier et choisir une réaction appropriée lorsqu’un incident est avéré. Ces références ne prescrivent ni le nombre de tentatives, ni les droits métier de rejeu, ni les états de la file décrits ici.

Tester la reprise, pas seulement le parcours heureux

Les tests doivent provoquer les pannes aux frontières du système :

  • arrêt du consommateur après l’effet externe mais avant l’acquittement ;
  • expiration du délai de visibilité pendant un traitement long ;
  • redélivrance simultanée du même événement ;
  • réponse distante ambiguë ou délai dépassé ;
  • donnée définitivement invalide ;
  • indisponibilité prolongée dépassant le budget de retry ;
  • rejeu manuel effectué deux fois ;
  • impossibilité d’accéder à la charge utile mise en quarantaine.

Pour chaque scénario, vérifiez l’état final, le nombre réel d’effets métier, l’historique des transitions, l’alerte générée et la capacité d’un opérateur à comprendre le cas. Testez également la restauration du registre et la politique de rétention. Une file durable n’est utile que si sa configuration, ses clés et ses données associées restent disponibles.

Checklist de mise en production

  • Chaque événement possède un identifiant stable et une clé d’idempotence adaptée à son effet métier.
  • Les erreurs sont classées en transitoires, permanentes, ambiguës ou critiques.
  • Les retries ont un plafond, un backoff, un jitter et une durée maximale.
  • Le délai de visibilité ou d’acquittement dépasse le temps normal de traitement, avec une marge mesurée.
  • La DLQ ou la quarantaine dispose d’une rétention suffisante et d’une alerte.
  • Le registre conserve le motif, les tentatives, les transitions, l’opérateur et la preuve de résolution.
  • Le rejeu repasse par les contrôles d’idempotence.
  • Les erreurs critiques arrêtent automatiquement les retries.
  • Les droits de consultation, de correction, de rejeu et de validation sont séparés.
  • Les journaux ne contiennent ni secrets ni données personnelles inutiles.
  • Un responsable opérationnel et un délai de traitement sont définis pour chaque quarantaine.
  • Les scénarios de redélivrance, panne partielle, doublon et restauration ont été testés.

Si vous êtes encore au stade du choix des processus, commencez par les tâches administratives à automatiser en priorité. Si votre workflow existe déjà mais accumule des incidents difficiles à diagnostiquer, le diagnostic des erreurs qui rendent une automatisation fragile aidera à identifier les points faibles avant d’ajouter la reprise.

Détail d’un poste de travail pour Concevoir une file de reprise pour les erreurs d'automatisation
Concevoir une file de reprise pour les erreurs d'automatisation — rendre les règles, les exceptions et les contrôles visibles.

La reprise est une propriété du produit

Une automatisation fiable n’est pas celle qui ne rencontre jamais d’erreur. C’est celle qui transforme chaque échec en dossier borné, compréhensible et résoluble. L’identifiant relie les tentatives, l’état rend le parcours visible, le motif guide la décision, l’idempotence limite les doublons, la quarantaine protège le flux et la preuve de résolution permet de clôturer sans ambiguïté.

La bonne architecture dépendra du risque, du volume et des outils de l’entreprise. Mais une règle reste valable : si personne ne sait qui doit traiter une erreur, comment la rejouer et comment vérifier le résultat, la reprise n’est pas encore conçue.

Sources et références

  1. [1] Using dead-letter queues in Amazon SQS - AWS Documentation
  2. [2] Configure a dead-letter queue using the Amazon SQS console
  3. [3] Amazon SQS visibility timeout
  4. [4] Retry pattern - Azure Architecture Center | Microsoft Learn
  5. [5] Retry Storm antipattern - Azure Architecture Center
  6. [6] Subscription retry policy | Pub/Sub
  7. [7] [PDF] RECOMMANDATIONS DE SÉCURITÉ POUR L'ARCHITECTURE D ...
  8. [8] Les règles de sécurité - ANSSI
  9. [9] ANSSI — Piloter un projet de supervision de sécurité (12 septembre 2025)
  10. [10] RFC 9110 — HTTP Semantics, méthodes idempotentes