Cloud
Azure Service Bus : diagnostiquer une perte de verrou avant d'allonger l'auto-renouvellement
Un runbook de production pour séparer durée de traitement, perte de connexion, échec de renouvellement et settlement tardif avant d'allonger un verrou Service Bus.
Un worker Azure Service Bus termine une réconciliation de paiement, puis échoue au moment de compléter le message avec une exception de verrou perdu. Le message revient dans la file, une autre instance le reçoit et l’action métier risque de s’exécuter deux fois. Allonger l’auto-renouvellement semble être le correctif le plus rapide. Cette mesure peut aussi masquer une dépendance lente, un event loop bloqué, des connexions instables ou un handler dont les effets ne sont pas idempotents.
Le cas fil rouge utilise un consumer PeekLock sur payments-reconcile-prod. La plupart des messages finissent en 20 secondes, mais une petite cohorte prend plusieurs minutes. Le runbook sépare verrou initial de l’entité, renouvellement client, traitement et settlement. Il se termine par une décision bornée : raccourcir ou découper le travail, ajuster le renouvellement, réparer la connectivité, mettre une famille de messages en quarantaine ou rollbacker le worker.
Figer une livraison avant de modifier le verrou
Capturez une livraison en échec, de la réception au settlement. Relevez namespace, entité, message ID, delivery count, sequence number, instance du worker, version déployée, heure de réception, LockedUntil, identifiant d’opération métier, heure de fin et exception exacte.
Ne rejouez pas encore le message et n’augmentez pas LockDuration. La première question est de savoir si le handler a perdu le verrou avant, pendant ou après l’effet métier.
entity: payments-reconcile-prod
receive_mode: PeekLock
message_id: pay-20260928-1842
delivery_count: 3
worker_instance: reconcile-7d8f9
release: 2026.09.28.4
received_utc: 2026-09-28T14:02:11Z
locked_until_utc: 2026-09-28T14:03:11Z
business_commit_utc: 2026-09-28T14:04:37Z
settlement_utc: 2026-09-28T14:04:38Z
result: MessageLockLost
side_effect_key: reconciliation/pay-20260928-1842 Une exception de verrou perdu ne prouve pas que l’action métier a échoué. Elle prouve que le receiver n’a pas pu effectuer le settlement avec le lock token qu’il détenait. Traitez l’effet externe et le settlement broker comme deux faits distincts.
Lire le contrat de l’entité
Inspectez la queue ou la subscription réellement déployée, plutôt qu’un réglage applicatif ou un souvenir du portail.
RG="rg-messaging-prod"
NAMESPACE="sb-orders-prod"
QUEUE="payments-reconcile-prod"
az servicebus queue show --resource-group "$RG" --namespace-name "$NAMESPACE" --name "$QUEUE" --query "{lockDuration:lockDuration,maxDeliveryCount:maxDeliveryCount,deadLetterOnExpiration:deadLetteringOnMessageExpiration,status:status}" --output yaml En PeekLock, l’entité accorde un verrou initial. Completion, abandon, deferral et dead-letter règlent le message tant que ce verrou reste valide. À son expiration, le message redevient disponible. Les livraisons répétées peuvent finir en dead-letter queue selon MaxDeliveryCount.
Un verrou initial plus long n’est pas gratuit. Si un worker meurt, le suivant attend davantage avant de récupérer le message. Gardez une durée supérieure au traitement normal, mais réservez le renouvellement aux travaux réellement longs et bornés.
Séparer quatre familles de panne
Un diagnostic utile distingue :
- dépassement du traitement : la durée du handler dépasse le verrou ou la fenêtre d’auto-renouvellement ;
- renouvellement affamé : thread pool saturé, event loop bloqué, pression CPU ou processus gelé empêchent le renouvellement de s’exécuter à temps ;
- perte de connexion ou de receiver : le lien AMQP, le receiver ou le client est fermé, recréé ou déconnecté avant le settlement ;
- settlement tardif : le travail est terminé, mais le handler règle le message après annulation, arrêt ou expiration.
Une modification de l’entité, une mise à jour du service ou une perte de connexion peuvent aussi invalider ces verrous volatils. Allonger uniquement le renouvellement n’est donc justifié qu’après avoir prouvé qu’il reste sain et que le traitement long est intentionnel.
Mesurer l’enveloppe de traitement
Instrumentez réception, premier effet, dernier effet et settlement sous forme de spans ou d’événements structurés distincts. Incluez MessageId, DeliveryCount, LockedUntil, instance et release.
let Window = 6h;
AppTraces
| where TimeGenerated > ago(Window)
| where Properties["Entity"] == "payments-reconcile-prod"
| extend
MessageId = tostring(Properties["MessageId"]),
Phase = tostring(Properties["Phase"]),
DeliveryCount = toint(Properties["DeliveryCount"]),
Worker = tostring(Properties["WorkerInstance"]),
Release = tostring(Properties["Release"])
| summarize
Received=minif(TimeGenerated, Phase == "received"),
BusinessCommitted=maxif(TimeGenerated, Phase == "business_committed"),
Settled=maxif(TimeGenerated, Phase == "settled"),
LockLost=countif(Phase == "lock_lost"),
MaxDelivery=max(DeliveryCount)
by MessageId, Worker, Release
| extend
ProcessingSeconds=datetime_diff("second", BusinessCommitted, Received),
SettlementLagSeconds=datetime_diff("second", Settled, BusinessCommitted)
| order by LockLost desc, ProcessingSeconds desc Comparez p50, p95 et p99 à la durée initiale et à la durée maximale de renouvellement. Découpez ensuite par type de message, dépendance, release et instance. Une longue traîne limitée à un type de document demande de façonner le workload ; une instance entière sans renouvellement pointe vers la santé du runtime.
Les métriques de plateforme posent une autre frontière. Comparez messages complétés et abandonnés, backlog actif et dead-lettered, erreurs serveur et churn de connexions sur la même fenêtre UTC. Les métriques décrivent la tendance de l’entité ; les traces applicatives expliquent quel handler détenait chaque verrou.
Prouver que le renouvellement s’exécute
Lisez la configuration SDK effective au démarrage et exposez-la dans le diagnostic de déploiement. Avec le client .NET actuel, MaxAutoLockRenewalDuration borne le temps pendant lequel le processor renouvelle automatiquement les verrous. Ce n’est ni la durée de chaque extension, ni une garantie d’idempotence.
var options = new ServiceBusProcessorOptions
{
ReceiveMode = ServiceBusReceiveMode.PeekLock,
AutoCompleteMessages = false,
MaxConcurrentCalls = 8,
MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(10)
};
logger.LogInformation(
"ServiceBus processor configured: mode={Mode}, concurrent={Concurrent}, renewal={Renewal}",
options.ReceiveMode,
options.MaxConcurrentCalls,
options.MaxAutoLockRenewalDuration); Ne copiez pas dix minutes comme valeur universelle. Dimensionnez la fenêtre depuis un budget de traitement mesuré, avec marge, puis alertez lorsque le travail s’en approche. Si une tâche ne possède aucune borne crédible, placez l’opération longue derrière un workflow durable et faites persister rapidement l’intention par le handler Service Bus.
Inspectez aussi les chemins d’annulation et de disposal. Un déploiement qui arrête le processor avant le settlement des messages en vol peut créer une grappe de pertes de verrou alors que le traitement nominal reste rapide.
Rendre la redelivery sûre avant de régler
PeekLock fournit un traitement au moins une fois, pas des effets métier exactement une fois. Utilisez une clé métier stable ou le message ID pour protéger l’action externe. Conservez un état comme started, committed et settled-pending dans un système offrant la cohérence requise.
Lors d’une redelivery :
- si aucune opération n’existe, réservez la clé puis traitez ;
- si l’opération est commitée, sautez l’effet et complétez le message ;
- si l’état est ambigu, mettez en quarantaine ou réconciliez au lieu de répéter ;
- si l’essai précédent a échoué avant commit, reprenez uniquement depuis une frontière connue.
Ne passez pas à ReceiveAndDelete pour supprimer les exceptions de verrou. Vous échangeriez les doublons contre une perte possible si le worker tombe après réception.
Canaryer une seule correction
Choisissez une famille de messages ou une file de canari dédiée. Gardez la concurrence de production inchangée et ne modifiez qu’une variable : découpage du handler, durée de renouvellement, timeout de dépendance ou capacité du worker.
Le canari doit prouver :
- un message normal s’exécute une fois et se règle avant l’expiration initiale ;
- un message long contrôlé renouvelle son verrou et s’exécute une fois ;
- un worker tué provoque une redelivery sans dupliquer l’effet métier ;
- un timeout de dépendance arrive avant épuisement du budget de renouvellement ;
- l’arrêt de déploiement draine ou abandonne les messages en vol de façon prévisible.
Observez plusieurs périodes de verrou et au moins un échec contrôlé. Comparez percentiles de traitement, erreurs de renouvellement, completion, abandon, delivery count et croissance de la dead-letter queue avec la release précédente.
Décider, valider ou rollbacker
Allongez l’auto-renouvellement uniquement si le traitement long est attendu, borné, observable et idempotent, et si le renouvellement continue sous une charge CPU et des conditions réseau réalistes. Raccourcissez ou découpez le handler lorsqu’il conserve un verrou broker en attendant un travail qui pourrait être persisté ailleurs. Réparez le runtime ou le réseau quand les renouvellements disparaissent sur plusieurs messages sans relation dans la même instance.
Rollbackez si le canari augmente les effets dupliqués, le delivery count, la dead-letter queue ou le temps d’arrêt. Restaurez les réglages et la release précédents, arrêtez les consumers candidats, puis vérifiez qu’un message connu est reçu, traité et réglé une seule fois. Gardez les livraisons ambiguës en quarantaine jusqu’à réconciliation de leur état métier.
Conclusion
Une perte de verrou Service Bus est d’abord un problème de chronologie, pas de durée. Reconstruisez réception, renouvellement, effet et settlement ; comparez la longue traîne au contrat déployé ; puis prouvez que la redelivery reste sûre.
La sortie opérationnelle est explicite : découper un traitement lent, réparer le renouvellement, allonger une fenêtre mesurée, isoler une cohorte dangereuse ou revenir au dernier worker connu. L’incident n’est clos que lorsque l’état du broker et l’état métier concordent.