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.

26 juil. 2026 azuredurable-functionsazure-functionsorchestrationautomationobservabilityapplication-insightskqlstorageidempotencyrunbookrollbackproduction

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.

yaml durable-orchestration-scope.yml
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.

bash 01-durable-instance-state.sh
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é.

kusto 02-durable-orchestration-timeline.kql
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.

text durable-failure-classes.txt
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.

yaml durable-idempotency-contract.yml
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é.

text task-hub-health-checks.txt
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.

text durable-decision-table.txt
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.

text post-durable-action-validation.txt
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.