Pratique SQL Server

Files SQL Server : réserver, valider et reprendre

Réservez un travail atomiquement, comprenez READPAST et empêchez un ancien worker d’écraser le résultat d’un nouveau propriétaire.

Un SELECT suivi d'un UPDATE distinct ne réserve pas un travail de manière sûre. Deux workers peuvent choisir la même ligne prête avant que l'un change son état. La file nécessite une transition atomique, suivie d'une validation courte. Le travail métier potentiellement lent intervient après cette validation.

Réserver par une seule instruction

Créez la table une fois dans une base jetable. Son index aide à trouver les travaux prêts sans parcourir continuellement tout l'historique terminé. Le contenu est volontairement petit ; une vraie file peut référencer un document volumineux conservé ailleurs.

CREATE TABLE dbo.QueueDemo
( JobId bigint IDENTITY PRIMARY KEY, State char(1) NOT NULL,
  ClaimToken uniqueidentifier NULL, LeaseUntil datetime2(3) NULL,
  Payload nvarchar(100) NOT NULL );
CREATE INDEX IX_QueueDemo_Ready ON dbo.QueueDemo(State, JobId);
INSERT dbo.QueueDemo(State, Payload) VALUES ('R', N'first'), ('R', N'second');

Exécutez la réservation sous READ COMMITTED sans transaction englobante. La garde explicite cette condition. UPDLOCK coordonne les réservations concurrentes, READPAST permet de passer certaines lignes verrouillées et READCOMMITTEDLOCK demande une lecture avec verrous lorsque la base utilise le versionnement Read Committed. Cette combinaison correspond à ce contrat d'isolation, pas à n'importe quelle transaction SNAPSHOT.

IF @@TRANCOUNT <> 0 OR (@@OPTIONS & 2) = 2
    THROW 50000, 'Use autocommit for this example.', 1;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
DECLARE @claimed table
(JobId bigint, ClaimToken uniqueidentifier, Payload nvarchar(100));
DECLARE @token uniqueidentifier = NEWID();
;WITH candidate AS
( SELECT TOP (1) * FROM dbo.QueueDemo
  WITH (UPDLOCK, READPAST, READCOMMITTEDLOCK)
  WHERE State = 'R' ORDER BY JobId )
UPDATE candidate
SET State = 'W', ClaimToken = @token,
    LeaseUntil = DATEADD(minute, 5, SYSUTCDATETIME())
OUTPUT inserted.JobId, inserted.ClaimToken, inserted.Payload
INTO @claimed;
SELECT * FROM @claimed;

La CTE ordonne les candidats par JobId, sans garantir un FIFO global. Une ancienne tâche verrouillée peut être ignorée et l'ordre des fins dépend de la durée du travail. READPAST ne contourne pas tous les verrous de page ou de schéma. Une sortie vide indique seulement que cette tentative n'a réservé aucune ligne disponible.

OUTPUT INTO capture l'identifiant et le jeton modifiés par la même instruction. Le SELECT final arrive après la fin de l'UPDATE en autocommit. Toute erreur d'exécution doit néanmoins être considérée comme un échec ; ne lancez pas un traitement sur une sortie partiellement observée. Testez plusieurs réservations simultanées et enregistrez chaque identifiant avec son jeton.

Gérer les travaux abandonnés

Le bail fixe une expiration, mais n'arrête pas physiquement le worker. Un processus suspendu peut reprendre après cette échéance. Chaque changement de propriétaire doit donc remplacer ClaimToken. La finalisation vérifie JobId et le jeton courant pour empêcher un ancien worker de terminer une nouvelle tentative.

-- Parameters supplied from the successful claim:
-- @JobId bigint, @ClaimToken uniqueidentifier
UPDATE dbo.QueueDemo
SET State = 'D', LeaseUntil = NULL
WHERE JobId = @JobId AND ClaimToken = @ClaimToken AND State = 'W';
SELECT @@ROWCOUNT AS CompletedRows;

Zéro ligne modifiée signifie que l'état ou la propriété a changé. Ce n'est pas un succès à ignorer silencieusement. Définissez les prolongations par heartbeat vérifiant le jeton, la durée maximale et le mécanisme de remise en attente des baux expirés. Le récupérateur doit lui aussi modifier conditionnellement les lignes et invalider l'ancien jeton. Une mise à jour aveugle après une lecture antérieure recrée la course initiale.

Prévoir les effets externes répétés

Un bail ne garantit pas l'exécution unique d'un courriel, d'un paiement ou d'un appel distant. Le worker peut terminer l'effet externe puis tomber avant d'enregistrer sa réussite. Après expiration, un autre recommencera. Utilisez un identifiant métier stable avec une destination idempotente, ou une procédure durable de rapprochement si la destination ne sait pas dédupliquer.

Conservez l'historique des tentatives et bornez les échecs. Un contenu définitivement invalide doit finir dans un état d'erreur accompagné d'un diagnostic exploitable. Surveillez l'ancienneté du plus vieux travail prêt, les baux expirés, les échecs répétés et la durée de traitement. La profondeur seule ne révèle pas une tâche importante durablement bloquée.

Avant déploiement, arrêtez un worker après réservation, retardez-en un au-delà du bail et perdez volontairement une réponse de finalisation. Vérifiez la récupération, le rejet des anciens jetons et l'absence de répétition d'effets métier. Ces essais expliquent la fiabilité mieux qu'une mesure de débit avec des workers qui ne rencontrent jamais de panne.

Références techniques: Microsoft Learn: Table hints · Microsoft Learn: OUTPUT.

Question sur cet article

Vous avez une question sur ce sujet ?

Expliquez ce que vous évaluez ou le point qui vous bloque. Nous vous répondrons avec une recommandation pratique.

Inquiries are not enabled in this preview.

Poser une question sur cet article