Automation
Azure Durable Functions : diagnostiquer une orchestration avant replay ou purge
Un runbook de production pour qualifier une orchestration Azure Durable Functions bloquée avec historique d'instance, état des activités, stockage, idempotence, KQL, validation et rollback avant replay, terminate ou purge.
Une orchestration Durable Functions qui reste Running, rejoue son historique, échoue après une activité ou n’atteint jamais sa sortie attendue ressemble vite à un simple problème de runtime. Les réponses rapides sont tentantes : rejouer l’événement, terminer l’instance, purger l’historique, redéployer la Function App ou démarrer une nouvelle orchestration avec la même entrée. En production, chacun de ces raccourcis peut dupliquer un effet de bord, masquer une activité poison, effacer les preuves ou laisser un système externe dans un état intermédiaire.
Le cas d’usage est un workflow de production qui coordonne plusieurs étapes : lire une demande, appeler des API internes, fan-out des activités, mettre à jour une base, envoyer une notification, tourner une valeur de configuration ou déclencher une remédiation contrôlée. L’objectif du runbook est de décider si l’instance doit continuer, être rejouée depuis une frontière connue, terminée, compensée, purgée après capture des preuves, ou bloquée jusqu’à correction du contrat d’orchestration.
Figer la frontière d’orchestration
Commence par une seule instance. Durable Functions peut exécuter beaucoup d’orchestrations et d’activités en parallèle, et le même chemin de code peut rester sain pour la plupart des entrées. La première action utile consiste à capturer la frontière d’instance avant tout changement d’état.
incident:
function_app: func-prod-automation
task_hub: prodhub
orchestration: OrderReconciliationOrchestrator
instance_id: order-20260726-0842
runtime: production
symptom: running_without_progress_or_failed_after_activity
last_known_healthy_instance: order-20260726-0810
state_to_freeze:
orchestration_input_hash
created_time
last_updated_time
runtime_status
custom_status
failed_activity
external_event_name
external_correlation_id
side_effects_already_observed
replay_or_termination_owner
rollback_or_compensation_reference Si l’équipe ne peut pas nommer l’instance et ses effets attendus, ne rejouez rien globalement. Un replay large peut traiter du nouveau travail, de l’ancien travail et du travail cassé dans le même mouvement.
Lire le statut avant de changer le statut
Utilisez l’état de l’instance Durable comme première source de vérité, puis corrélez avec Application Insights. La question importante n’est pas seulement de savoir si l’orchestration a échoué. Il faut savoir où elle s’est arrêtée et si la dernière activité a déjà pu modifier un système externe.
RESOURCE_GROUP="rg-automation-prod"
FUNCTION_APP="func-prod-automation"
INSTANCE_ID="order-20260726-0842"
az functionapp function show --resource-group "$RESOURCE_GROUP" --name "$FUNCTION_APP" --function-name "OrderReconciliationOrchestrator" --output json
curl -sS "https://$FUNCTION_APP.azurewebsites.net/runtime/webhooks/durabletask/instances/$INSTANCE_ID?code=<system-key>" | jq '{instanceId, runtimeStatus, createdTime, lastUpdatedTime, customStatus, output}' Ne mettez pas les secrets dans la note d’incident, mais conservez les timestamps, le statut, le custom status et les IDs de corrélation retournés. Si les endpoints de statut ne sont pas accessibles depuis le réseau opérateur, capturez la même preuve via le chemin de gestion approuvé.
Reconstruire la chronologie en KQL
Durable Functions rejoue le code orchestrateur par conception. Des logs orchestrateur qui semblent dupliqués ne prouvent donc pas toujours des actions métier doublonnées. Les logs d’activités, les appels de dépendances et les IDs de corrélation métier comptent davantage qu’un message répété.
let InstanceId = "order-20260726-0842";
union traces, exceptions, requests, dependencies
| where timestamp > ago(24h)
| where tostring(customDimensions["prop__instanceId"]) == InstanceId
or tostring(customDimensions["InstanceId"]) == InstanceId
or operation_Id == InstanceId
| project timestamp,
itemType,
operation_Id,
name,
message,
success,
resultCode,
DurationMs = duration,
FunctionName = tostring(customDimensions["prop__functionName"]),
State = tostring(customDimensions["prop__state"]),
Reason = tostring(customDimensions["prop__reason"])
| order by timestamp asc Cherchez la dernière activité non replay, le dernier appel de dépendance et le dernier effet externe. Si vous ne voyez que du bruit de replay orchestrateur, ajoutez des logs au niveau activité avant de conclure que le workflow boucle.
Séparer orchestration, activité et dépendance
Un incident Durable peut se situer dans plusieurs couches. Traiter chaque symptôme comme un bug orchestrateur mène à des purges risquées et à des redéploiements inutiles.
Echec de contrat orchestrateur
Code non deterministe modifie dans l'orchestrateur
Date, valeur aleatoire ou appel externe execute dans le code orchestrateur
Historique incompatible avec la logique d'orchestration deployee
Decision : stopper le rollout, restaurer un code compatible ou terminer avec compensation
Echec d'activite
Une activite renvoie exception, timeout ou sortie invalide
Les retries sont peut-etre epuises ou encore en cours
Decision : corriger dependance ou entree, puis rejouer depuis une frontiere connue
Attente d'evenement externe
L'instance attend une approbation, un callback ou un evenement jamais arrive
Decision : prouver la source de l'evenement avant d'envoyer un evenement de remplacement
Echec stockage ou task hub
Historique Durable, queues, leases ou tables ne sont pas lisibles ou modifiables de facon fiable
Decision : corriger stockage et sante du task hub avant replay ou purge
Effet de bord ambigu
Une activite a expire apres appel d'un systeme externe
Decision : interroger le systeme externe avant de retenter l'activite Cette classification protège de l’erreur la plus coûteuse : terminer ou purger l’instance alors que le vrai problème est un état de dépendance qui exige encore une compensation.
Vérifier l’idempotence avant replay
Durable Functions structure les retries, mais ne rend pas chaque activité idempotente. Un replay peut être sûr pour une lecture ou un calcul déterministe. Il peut être dangereux pour facturation, notification, provisioning, suppression, création de ticket, rotation de secret ou export de données.
orchestration: OrderReconciliationOrchestrator
instance_id: order-20260726-0842
idempotency:
orchestration_key: order_batch_id
activity_keys:
LoadOrders: batch_id
ReserveProcessingWindow: batch_id + window
UpdateBillingState: order_id + target_state
NotifyOperations: incident_id + notification_type
safe_to_replay_when:
- activity_has_no_external_side_effect
- external_system_reports_no_operation_created
- idempotency_key_already_maps_to_same_result
unsafe_to_replay_when:
- timeout_after_external_write
- missing_operation_id
- notification_or_ticket_created_without_correlation
- compensation_plan_unknown Si l’activité ne peut pas prouver son idempotence, le chemin le plus sûr est souvent une réconciliation manuelle ou une compensation, pas un replay automatique.
Contrôler stockage et task hub
L’orchestration dépend du compte de stockage du task hub. Backlog de queues, messages poison, throttling, changement de firewall, rotation de clé ou dérive de nom de task hub peuvent donner l’impression qu’un code sain est bloqué.
Controles task hub
AzureWebJobsStorage pointe vers le compte de stockage de production attendu
Le nom du task hub correspond a l'environnement deploye
Les queues control et work-item ne sont pas bloquees par des messages poison
Les tables History et Instances sont lisibles
Les changements firewall, chemin prive ou identite sont connus
Les logs host montrent que le runtime Functions acquiert les leases
Aucun deploiement n'utilise le meme task hub avec un code incompatible
Bloquer la purge quand
La sante du stockage est inconnue
Le task hub peut etre partage avec un autre environnement
L'historique d'instance est la seule preuve des effets de bord
Un redemarrage host pourrait permettre a l'instance de continuer proprement Le réseau privé peut faire partie des preuves quand le stockage est restreint, mais ce n’est qu’une frontière parmi d’autres. Le point clé est de prouver la santé du task hub avant de changer l’état d’orchestration.
Décider continuer, rejouer, terminer, compenser ou purger
Rendez la décision opérationnelle explicite. La purge est rarement la première décision. Elle supprime l’historique seulement après que l’équipe a établi que cet historique n’est plus nécessaire pour récupérer ou auditer.
Laisser continuer
Runtime et stockage sont retablis
Les retries d'activite restent dans la policy attendue
Aucun effet doublon n'est possible
L'echeance metier permet encore l'attente
Rejouer depuis une frontiere connue
L'activite echouee est idempotente ou l'etat externe prouve l'absence d'effet
Entree et dependance sont corrigees
Correlation ID et sortie attendue sont connus
Les post-checks distinguent ancien et nouvel essai
Terminer avec compensation
L'instance ne peut pas progresser sans risque
Un effet partiel existe
Compensation ou reconciliation manuelle sont documentees
Une nouvelle orchestration dupliquerait sinon le travail
Demarrer une instance de remplacement
L'instance initiale est bloquee par une entree manquante ou un code obsolete
L'entree de remplacement est bornee et liee a l'instance initiale
L'ancienne instance est terminee ou conservee avec un proprietaire clair
Purger seulement apres capture des preuves
L'instance est completed, failed ou terminated sans valeur restante de recuperation
Chronologie, hash d'entree, effets de bord et decision sont stockes hors historique Durable
La purge est limitee a des instance IDs ou a une fenetre precise Une bonne décision indique autant ce qu’il ne faut pas faire que ce qu’il faut faire. Ne purgez pas pour nettoyer un dashboard. Ne redémarrez pas la Function App pour masquer un changement orchestrateur non déterministe.
Valider après action
Après continuation, replay, terminaison ou remplacement, validez ensemble l’état cible et le plan de contrôle Durable.
Validation cible
Le systeme externe contient une seule operation attendue par cle d'idempotence
Aucun doublon de notification, ticket, mise a jour de facturation ou configuration n'existe
L'etat metier correspond a l'etat final attendu ou a la compensation documentee
Les consommateurs aval n'attendent pas silencieusement l'ancienne instance
Validation Durable
Le statut de l'instance initiale est connu et consigne
L'instance de remplacement, si elle existe, reference l'instance initiale
Queues et historique du task hub sont sains
La chronologie Application Insights montre la decision finale
La note d'incident conserve KQL, statut d'instance, effets de bord et reference de rollback Si la validation ne peut pas prouver ces points, l’incident doit rester ouvert. Un statut d’orchestration vert ne suffit pas quand le workflow a déjà touché des systèmes externes.
Conclusion
Une orchestration Durable Functions est à la fois du code et de l’état opérationnel. Replay, terminate et purge sont des actions de production, pas des boutons de nettoyage. Le chemin sûr commence par la frontière d’instance, reconstruit la chronologie, sépare orchestrateur, activité, dépendance et stockage, puis prouve l’idempotence avant tout replay.
La décision devient défendable quand l’équipe peut dire : continuer parce que les retries sont sûrs, rejouer parce que la frontière est connue, terminer parce que la compensation est prête, remplacer parce que l’ancienne instance est bornée, ou purger parce que les preuves sont déjà préservées. C’est ce qui garde Durable Functions exploitable comme moteur d’automatisation fiable, au lieu d’un workflow opaque que les opérateurs hésitent à toucher.