3 votes

SQL Server déclenche une mise à jour asynchrone à partir du déclencheur ?

Si un utilisateur insère des lignes dans une table, j'aimerais que SQL Server effectue un traitement supplémentaire - mais pas dans le contexte de la transaction de l'utilisateur.

par exemple L'utilisateur donne accès en lecture à un dossier:

UPDATE Dossiers SET AccesLecture = 1
WHERE IDdossier = 7

Autant que l'utilisateur est concerné, je veux que ce soit la fin de l'opération atomique. En réalité, je dois maintenant trouver tous les fichiers et dossiers enfants et leur donner AccesLecture.

EXECUTER SynchroniserPermissions

C'est une opération potentiellement longue (plus de 2s). je veux que cette opération longue se produise "plus tard". Elle peut se produire 0 secondes plus tard, et avant que l'unité de carbone n'ait la chance d'y penser, la mise à jour asynchrone est terminée.

Comment puis-je exécuter cette opération requise de manière asynchrone lorsque c'est nécessaire (c'est-à-dire déclenché)?

L'idéal serait:

CREATE TRIGGER dbo.Dossiers POUR INSERT, UPDATE, DELETE COMME
   EXECUTERASYNCHRONEMENT SynchroniserPermissions

ou

CREATE TRIGGER dbo.Dossiers POUR INSERT, UPDATE, DELETE COMME
   EXECUTER SynchroniserPermissions AVEC(ASYNC)

Actuellement cela se produit comme un déclencheur:

CREATE TRIGGER dbo.Dossiers POUR INSERT, UPDATE, DELETE COMME
   EXECUTER SynchroniserPermissions

et l'utilisateur est obligé d'attendre les 3 secondes chaque fois qu'il apporte une modification à la table Dossiers.

J'ai pensé à créer une Tâche planifiée sur l'utilisateur, qui s'exécute toutes les minutes, et à vérifier un indicateur BesoindesautorisationsSynchronisation:

CREATE TRIGGER dbo.Dossiers POUR INSERT, UPDATE, DELETE COMME
   UPDATE ÉtatSystème SET BesoindesautorisationsSynchronisation = 1

Le binaire de la tâche planifiée peut vérifier cet indicateur, s'exécuter si l'indicateur est activé:

DÉCLARER @ValeurIndicateur int
SET @ValeurIndicateur = 0;

UPDATE ÉtatSystème SET @ValeurIndicateur = BesoindesautorisationsSynchronisation+1
WHERE BesoindesautorisationsSynchronisation = 1

SI @ValeurIndicateur = 2
DEBUT
   EXECUTER SynchroniserPermissions

   UPDATE ÉtatSystème SET BesoindesautorisationsSynchronisation = 0
   WHERE BesoindesautorisationsSynchronisation = 2
FIN

Le problème avec une tâche planifiée est: - elle ne peut fonctionner plus vite que toutes les 60 secondes - elle souffre d'être une solution de polling - elle nécessite un exécutable

Ce que je préférerais, c'est un moyen pour que SQL Server puisse déclencher la tâche planifiée:

CREATE TRIGGER dbo.Dossiers POUR INSERT, UPDATE, DELETE COMME
   EXECUTER SynchroniserPermissionsAsychronement

CREATE PROCEDURE dbo.SynchroniserPermissionsAsychronement COMME

   EXECUTER sp_ms_StartWindowsScheduledTask @taskName="SynchronousPermissions"

Le problème avec cela est: - il n'existe pas de procédure stockée système sp_ms_StartWinodowsScheduledTask

Donc je cherche des idées pour de meilleures solutions.


Mise à jour: L'exemple précédent est un problème qui n'a aucune bonne solution depuis cinq ans maintenant. Un problème datant de 3 ans, qui n'a pas de bonne solution est une table que je dois mettre à jour une colonne de métadonnées après une insertion/modification. Le calcul des métadonnées prend trop de temps dans le traitement en ligne, mais cela ne me dérange pas qu'il apparaisse 3 ou 5 secondes plus tard:

CREATE TRIGGER dbo.UpdateValeursTransfertFonds POUR INSERT, UPDATE AS

UPDATE TransfertsFonds
SET ValeurCommandeTotale = (SELECT ....[snip]....),
    ValeurDépôtTotale = (SELECT ....,[snip]....)
WHERE TransfertsFonds.IDTransfertFonds IN (
    SELECT i.IDTransfertFonds
    FROM INSERTED i
)

Et le problème que j'ai aujourd'hui est un moyen de mettre à jour de manière asynchrone certaines métadonnées après l'insertion ou la modification d'une ligne:

CREATE TRIGGER dbo.UpdateValeurCDR POUR INSERT, UPDATE AS

UPDATE LCDs
SET ValeurCDR = (SELECT ....[snip]....)
WHERE LCDs.GUIDLCD IN (
    SELECT i.GUIDLCD
    FROM INSERTED i
)

Mise à jour 2: J'ai pensé à créer une dll native ou gérée et à l'utiliser comme une procédure étendue stockée. Le problème avec cela est:

  • vous ne pouvez pas scripter un binaire
  • je n'y suis pas autorisé

7voto

Aaron Bertrand Points 116343

Utilisez une table de file d'attente et faites en sorte qu'un processus en arrière-plan différent récupère les éléments de la file d'attente et les traite. Le déclencheur lui-même fait partie de la transaction de l'utilisateur par définition - c'est précisément pourquoi ils sont souvent déconseillés (ou du moins les gens sont avertis de ne pas utiliser des techniques coûteuses à l'intérieur des déclencheurs).

1voto

SQLMenace Points 68670

Créez un travail SQL Agent et exécutez-le avec sp_start_job... il ne doit pas attendre la fin

Cependant, vous avez besoin des autorisations appropriées pour exécuter des tâches

Les membres de SQLAgentUserRole et SQLAgentReaderRole ne peuvent démarrer que des tâches qu'ils possèdent. Les membres de SQLAgentOperatorRole peuvent démarrer toutes les tâches locales y compris celles appartenant à d'autres utilisateurs. Les membres de sysadmin peuvent démarrer toutes les tâches locales et multiserveur.

Le problème avec cette approche est que si le travail est déjà en cours, il ne peut pas être démarré avant qu'il ne soit terminé

Sinon, suivez la suggestion de la table de file d'attente d'Aaron, c'est plus propre et mieux

0voto

Nous avons rencontré ce problème il y a quelque temps, et j'ai trouvé une solution qui fonctionne parfaitement. J'ai un processus en arrière-plan - mais tout comme vous, je ne voulais pas qu'il doive surveiller toutes les 60 secondes.

Voici les étapes:

(1) Notre déclencheur n'exécute pas la mise à jour de la base de données lui-même. Il place simplement un "fichier d'indication" dans un dossier surveillé par le processus en arrière-plan.

(2) Le processus en arrière-plan surveille ce dossier en utilisant la Notification de Changement de Windows (c'est là que ça devient vraiment cool, car vous n'avez pas à surveiller le dossier - votre processus dort jusqu'à ce que Windows le notifie qu'un fichier est apparu). Chaque fois que le processus en arrière-plan est réveillé par Windows, il exécute la mise à jour de la base de données. Ensuite, il supprime le(s) fichier(s) d'indication, se rendort et demande à Windows de le réveiller lorsque qu'un autre fichier apparaît dans le dossier.

Cela fonctionne exactement comme vous l'avez décrit: la mise à jour déclenchée s'exécute peu de temps après l'événement principal de la base de données, et voilà, l'utilisateur n'a pas à attendre les quelques secondes supplémentaires. J'adore ça.

Vous n'avez pas nécessairement besoin de compiler votre propre exécutable pour faire cela : de nombreux langages de script peuvent utiliser la Notification de Changement de Windows. J'ai écrit le processus en arrière-plan en Perl et il m'a fallu seulement quelques minutes pour le faire fonctionner.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X