Automation
Azure Logic Apps : diagnostiquer des exécutions en double avant de désactiver les retries
Un runbook de production pour distinguer rediffusion, splitOn, concurrence et retry ambigu, puis imposer l'idempotence sans perdre la résilience du workflow.
Un workflow Azure Logic Apps crée deux commandes aval pour un seul événement métier. Les deux runs sont en succès, leurs heures de départ sont proches et le système source n’affiche qu’une action utilisateur. Le premier réflexe consiste à désactiver les retries ou à limiter la concurrence à un. Ces changements peuvent réduire le symptôme, mais ils ne prouvent ni l’origine du doublon ni l’absence d’une nouvelle duplication lors du prochain timeout.
Le cas fil rouge est un workflow qui reçoit un événement de commande, enrichit la charge utile puis appelle une API interne. Azure Logic Apps fournit les messages selon un modèle au moins une fois : une rediffusion rare doit donc rester compatible avec l’intégrité métier. L’objectif du runbook est de relier chaque effet aval à un événement, un déclenchement, un run et une tentative d’action, puis de décider entre correction de la source, ajustement de débit, barrière d’idempotence ou rollback.
Figer le doublon avant de modifier le workflow
Commencez par une paire d’effets réellement dupliqués. Conservez l’identifiant métier, les heures UTC, les identifiants des deux runs, l’historique du déclencheur, la révision du workflow et les identifiants retournés par la cible. Deux lignes identiques dans un dashboard ne suffisent pas : elles peuvent représenter deux événements légitimes, deux affichages d’un même effet ou deux écritures distinctes.
business_event:
event_id: order-84721
entity_id: customer-order-392
produced_at_utc: 2026-09-24T08:14:02Z
workflow:
resource: la-orders-prod
name: process-order
definition_version: git-sha-or-release-id
runs:
- run-a
- run-b
downstream_effects:
- request_id: api-request-1901
result_id: fulfillment-551
- request_id: api-request-1908
result_id: fulfillment-552
temporary_guards:
- do not resubmit either run
- do not purge run history
- do not disable retries globally
- stop manual replay for this event Exportez les entrées et sorties utiles en masquant secrets et données personnelles. Notez aussi les changements récents sur le trigger, splitOn, la concurrence, la retry policy, les boucles et l’API cible. Le diagnostic doit survivre à l’expiration de l’historique de runs.
Construire la chaîne d’identifiants
Un doublon devient explicable quand quatre niveaux sont reliés : l’événement métier produit par la source, l’occurrence du trigger, le run du workflow et la tentative envoyée au système aval. Ajoutez un identifiant stable à la source lorsqu’il existe déjà dans le domaine, puis propagez-le sans le recalculer.
Event ID Trigger occurrence Workflow run Action attempt Downstream result
order-84721 trigger-301 run-a attempt-1 fulfillment-551
order-84721 trigger-302 run-b attempt-1 fulfillment-552
Questions
- La source a-t-elle émis deux fois le même event ID ?
- Un seul trigger a-t-il créé plusieurs runs via splitOn ?
- Un run unique a-t-il répété l'action après timeout ?
- La cible a-t-elle traité deux fois la même clé métier ? Ne remplacez pas l’identifiant métier par le nom du run. Un nouveau run créé par rediffusion porte un nouvel identifiant technique alors qu’il représente la même intention. À l’inverse, deux événements métier différents ne doivent pas être fusionnés parce que leur payload visible se ressemble.
Séparer les quatre chemins de duplication
Le premier chemin se trouve en amont : webhook envoyé deux fois, événement republie après perte d’accusé de réception, poller qui relit une fenêtre recouvrante ou producteur qui ne conserve pas son identifiant. Les deux runs ont alors deux occurrences de trigger, mais la même clé métier.
Le deuxième chemin vient du débatching. Avec splitOn, chaque élément d’un tableau démarre un run distinct. Vérifiez le payload exact et l’expression de découpage. Deux éléments portant la même clé créent volontairement deux runs ; limiter la concurrence ne les fusionne pas.
Le troisième chemin est un chevauchement de runs. Deux occurrences valides peuvent s’exécuter en parallèle et lire un état encore inchangé avant d’écrire chacune. Régler la concurrence à un peut sérialiser le workflow, mais ne protège pas contre une rediffusion ultérieure ou plusieurs instances d’un autre producteur.
Le quatrième chemin est le retry ambigu. Une action HTTP expire côté Logic Apps alors que la cible a terminé l’écriture. Le retry renvoie ensuite la même intention. Désactiver les retries évite cette seconde tentative, mais transforme les erreurs transitoires et les 429 en pertes ou en interventions manuelles. La correction durable se situe à la frontière qui produit l’effet.
Lire les retries comme des tentatives, pas comme des runs
Ouvrez les deux runs et localisez la première divergence. Si chaque run contient une seule tentative réussie, cherchez du côté du trigger ou de la source. Si un seul run présente plusieurs tentatives sur l’action avec un timeout, un 408, un 429 ou une erreur 5xx, corrélez chaque tentative avec les logs de l’API. Un statut en échec côté orchestrateur ne prouve pas l’absence d’effet côté serveur.
Figez la retry policy effective, y compris la valeur par défaut du connecteur. Comparez aussi la durée du timeout client avec le temps de traitement de la cible. Une API qui écrit puis répond trop tard crée une zone d’ambiguïté ; augmenter seulement le timeout la déplace sans rendre l’opération idempotente.
Poser l’idempotence sur la frontière métier
La cible doit reconnaître une intention déjà traitée et retourner son résultat initial sans recréer l’effet. La clé doit être stable pour une même opération métier et différente pour une nouvelle opération volontaire. Utilisez par exemple orderId + operation + version, pas un timestamp ni le run ID.
{
"eventId": "order-84721",
"operation": "create-fulfillment",
"entityVersion": 3,
"idempotencyKey": "order-84721:create-fulfillment:v3",
"correlation": {
"workflowRun": "run-a",
"action": "Create_fulfillment"
}
} La barrière doit être atomique. Une lecture « cette clé existe-t-elle ? » suivie d’une écriture séparée reste vulnérable à deux runs concurrents. Préférez une création conditionnelle, une contrainte unique ou une transaction qui enregistre la clé et l’effet ensemble. Conservez le résultat, le statut et une durée de rétention compatible avec la fenêtre maximale de rediffusion.
Si la cible ne peut pas évoluer immédiatement, ajoutez une barrière avant l’action dans un stockage partagé. Traitez-la comme une mesure compensatoire : panne du stockage, expiration trop courte, état in_progress bloqué et reprise après crash doivent avoir un comportement défini.
absent -> in_progress -> completed
-> failed_retryable
-> failed_final
On duplicate key
completed return stored result; do not write again
in_progress wait or return accepted; do not start a second write
failed_retryable retry with the same key and bounded ownership
failed_final stop and require an explicit decision Canaryer sans créer un troisième effet
Déployez d’abord la propagation de la clé et l’observation, sans supprimer les retries. Sur une commande de test contrôlée, envoyez deux fois le même événement puis provoquez, si possible, une réponse tardive après une écriture réussie. Le résultat attendu est un seul effet aval, deux traces corrélées et une réponse stable pour la seconde demande.
Exécutez ensuite un test négatif : deux opérations légitimes sur la même entité avec des versions ou intentions différentes doivent toutes deux passer. Une clé trop large évite les doublons en bloquant aussi les changements réels.
Surveillez pendant le canari le nombre d’événements, de triggers, de runs, de tentatives et d’effets aval par clé. Ces compteurs n’ont pas besoin d’être égaux : splitOn et les retries peuvent augmenter les niveaux intermédiaires. La propriété à préserver est un effet métier par intention idempotente.
Décider, valider ou rollbacker
Corrigez la source lorsque deux événements identiques sont produits sans raison. Corrigez splitOn lorsque le tableau ou l’expression crée des unités de travail inattendues. Réduisez la concurrence lorsque la cible sature, mais ne la présentez pas comme une garantie d’unicité. Conservez les retries pour les erreurs transitoires lorsque la frontière aval sait absorber une répétition.
Roll backez si la nouvelle clé fusionne des opérations distinctes, si le stockage d’idempotence devient un point de panne ou si les réponses rejouées ne correspondent pas au résultat initial. Restaurez la version précédente du workflow, bloquez les resubmissions et maintenez une procédure manuelle bornée pendant la correction. Ne supprimez pas les enregistrements d’idempotence déjà créés : ils protègent encore contre des événements en vol.
La validation finale tient en trois preuves : un replay du même événement ne crée pas de nouvel effet, une nouvelle intention légitime reste acceptée et un timeout après écriture converge vers le résultat existant. Documentez la durée de rétention de la clé, son propriétaire et le moyen de résoudre un état bloqué.
Conclusion
Une exécution Logic Apps en double n’est pas automatiquement un retry défectueux. Elle peut naître à la source, dans splitOn, dans un chevauchement de runs ou dans une réponse perdue après un effet réel. La chaîne d’identifiants permet de trouver le premier niveau qui se duplique au lieu de modifier plusieurs réglages à l’aveugle.
Le runbook se termine par une décision observable : réparer le producteur, corriger le débatching, borner la concurrence, rendre la cible idempotente ou rollbacker la barrière. La résilience reste utile quand répéter une tentative ne signifie plus répéter l’effet métier.