Comment et quand l'Agent SQL met-il à jour les valeurs next_run_date / next_run_time?

13

J'ai travaillé sur du code dans T-SQL pour ajouter de nouvelles planifications à un travail d'agent SQL en utilisant le proc sp_add_jobschedule dans la base de données msdb. Lorsque j'ajoute une nouvelle planification (généralement une exécution unique à une date / heure spécifique) et que je regarde immédiatement les valeurs dans sysjobschedules et sysschedules, je peux voir que la nouvelle planification a été ajoutée et est liée au job_id de mon agent SQL emploi. Cependant, les valeurs de next_run_date et next_run_time contiennent 0. Quand je reviens et les regarde à nouveau dans 2 ou 3 minutes, ils montrent toujours des 0 en eux. Cependant, lorsque je reviens 5 ou 10 minutes plus tard, il affiche désormais correctement les valeurs de date et d'heure correspondant à la prochaine exécution planifiée.

Mes questions sont donc:

  • À quelle fréquence ces valeurs sont-elles mises à jour?
  • Quel processus met à jour ces valeurs?
  • Si je devais ajouter un calendrier qui serait, disons, d'une minute dans le futur, cela signifie-t-il que le travail ne s'exécutera pas puisque la date_prochaine_exécution / heure n'a pas encore été mise à jour?

Exemple de code que j'utilise pour ajouter un nouveau planning:

exec msdb.dbo.sp_add_jobschedule @job_id = @jobID
                    , @name = @JobName
                    , @enabled = 1
                    , @freq_type = 1
                    , @freq_interval = 0
                    , @freq_subday_type = 0
                    , @freq_subday_interval = 0
                    , @freq_relative_interval = 0
                    , @freq_recurrence_factor = 0
                    , @active_start_date = @ScheduleRunDate
                    , @active_end_date = 99991231
                    , @active_start_time = @ScheduleRunTime
                    , @active_end_time = 235959

où @jobID est un binaire (16) contenant le job_id du travail en question, @ScheduleRunDate et @ScheduleRunTime sont des INT avec la date et l'heure respectivement.

BBlake
la source
3
Il est mis à jour via un travail SQL Agent. Qui est mis à jour via un travail SQL Agent. Les récursifs s'unissent!
Aaron Bertrand
1
Mes excuses. Je n'ai pas eu l'occasion de revenir en arrière depuis un moment.
BBlake

Réponses:

16

Réponse courte

Il semble que les données msdb.dbo.sysjobschedulessoient mises à jour par un thread d'arrière-plan dans l'Agent SQL, identifié comme SQLAgent - Schedule Saver, toutes les 20 minutes (ou moins fréquemment, s'il xp_sqlagent_notifyn'a pas été appelé et qu'aucune tâche n'a été exécutée entre-temps).

Pour des informations plus précises, regardez next_scheduled_run_datedans msdb.dbo.sysjobactivity. Il est mis à jour en temps réel chaque fois qu'un travail est modifié ou qu'un travail est exécuté. En prime, le sysjobactivitystocke les données dans le bon sens (sous forme de colonne datetime), ce qui le rend beaucoup plus facile à utiliser que ces stupides INT.

Voilà la réponse courte:

Cela pourrait prendre jusqu'à 20 minutes avant que sysjobschedules reflète la vérité; cependant, sysjobactivity sera toujours à jour. Si vous voulez beaucoup plus de détails à ce sujet, ou comment je l'ai compris ...


Longue réponse

Si vous souhaitez suivre le lapin un instant, lorsque vous appelez sp_add_jobschedule, cette chaîne d'événements est mise en mouvement:

msdb.dbo.sp_add_jobschedule == calls ==> msdb.dbo.sp_add_schedule
                                         msdb.dbo.sp_attach_schedule

msdb.dbo.sp_attach_schedule == calls ==> msdb.dbo.sp_sqlagent_notify

msdb.dbo.sp_sqlagent_notify == calls ==> msdb.dbo.xp_sqlagent_notify

Maintenant, nous ne pouvons plus poursuivre le lapin, car nous ne pouvons pas vraiment voir ce qui se xp_sqlagent_notifypasse. Mais je pense que nous pouvons présumer que cette procédure étendue interagit avec le service d'agent et lui indique qu'il y a eu un changement dans ce travail et ce calendrier spécifiques. En exécutant une trace côté serveur, nous pouvons voir que, immédiatement, le SQL dynamique suivant est appelé par l'Agent SQL:

exec sp_executesql N'DECLARE @nextScheduledRunDate DATETIME 
  SET @nextScheduledRunDate = msdb.dbo.agent_datetime(@P1, @P2) 
  UPDATE msdb.dbo.sysjobactivity 
    SET next_scheduled_run_date = @nextScheduledRunDate 
    WHERE session_id = @P3 AND job_id = @P4',
N'@P1 int,@P2 int,@P3 int,@P4 uniqueidentifier',
20120819,181600,5,'36924B24-9706-4FD7-8B3A-1F9F0BECB52C'

Il semble que la sysjobactivitymise à jour soit immédiate et sysjobschedulesne soit mise à jour que selon un calendrier. Si nous modifions le nouvel horaire pour qu'il soit une fois par jour, par exemple

@freq_type=4, 
@freq_interval=1, 
@freq_subday_type=1, 
@freq_subday_interval=0, 
@freq_relative_interval=0, 
@freq_recurrence_factor=1, 

Nous voyons toujours la mise à jour immédiate sysjobactivitycomme ci-dessus, puis une autre mise à jour une fois le travail terminé. Diverses mises à jour proviennent de l'arrière-plan et d'autres threads dans SQL Agent, par exemple:

SQLAgent - Job Manager
SQLAgent - Update job activity
SQLAgent - Job invocation engine
SQLAgent - Schedule Saver

Un thread d'arrière-plan (le thread "Schedule Saver") finit par arriver et se met à jour sysjobschedules; d'après mon enquête initiale, il semble que ce soit toutes les 20 minutes, et ne se produit que si xp_sqlagent_notifya été appelé en raison d'un changement apporté à un travail depuis la dernière exécution (je n'ai effectué aucun test supplémentaire pour voir ce qui se passe si un travail a été changé et un autre a été exécuté, si le fil de discussion "Schedule Saver" met à jour les deux - je soupçonne que cela doit, mais laissera cela comme un exercice au lecteur).

Je ne sais pas si le cycle de 20 minutes est décalé par rapport au démarrage de SQL Agent, ou à partir de minuit, ou par quelque chose de spécifique à la machine. Sur deux instances différentes sur le même serveur physique, le thread "Schedule Saver" a été mis à jour sysjobschedules, sur les deux instances, presque en même temps - 18:31:37 & 18:51:37 sur une seule, et 18:31:39 & 18:51:39 de l'autre. Je n'ai pas démarré SQL Server Agent en même temps sur ces serveurs, mais il est possible que les heures de démarrage soient décalées de 20 minutes à distance. J'en doute, mais je n'ai pas le temps pour le moment de confirmer en redémarrant l'agent sur l'un d'eux et en attendant que d'autres mises à jour se produisent.

Je sais qui l'a fait et quand cela s'est produit, parce que j'ai placé un déclencheur là-bas et l'ai capturé, au cas où je ne le trouverais pas dans la trace, ou que je l'ai filtré par inadvertance.

CREATE TABLE dbo.JobAudit
(
  [action] CHAR(1),
  [table] CHAR(1), 
  hostname SYSNAME NOT NULL DEFAULT HOST_NAME(), 
  appname SYSNAME  NOT NULL DEFAULT PROGRAM_NAME(),
  dt DATETIME2     NOT NULL DEFAULT SYSDATETIME()
);

CREATE TRIGGER dbo.schedule1 ON dbo.sysjobactivity FOR INSERT
AS
  INSERT dbo.JobAudit([action], [table] SELECT 'I', 'A';
GO
CREATE TRIGGER dbo.schedule2 ON dbo.sysjobactivity FOR UPDATE
AS
  INSERT dbo.JobAudit([action], [table] SELECT 'U', 'A';
GO
CREATE TRIGGER dbo.schedule3 ON dbo.sysjobschedules FOR INSERT
AS
  INSERT dbo.JobAudit([action], [table] SELECT 'I', 'S';
GO
CREATE TRIGGER dbo.schedule4 ON dbo.sysjobschedules FOR UPDATE
AS
  INSERT dbo.JobAudit([action], [table] SELECT 'U', 'S';
GO

Cela dit, il n'est pas difficile d'attraper avec une trace standard, celle-ci apparaît même en tant que DML non dynamique:

UPDATE msdb.dbo.sysjobschedules 
  SET next_run_date = 20120817, 
      next_run_time = 20000 
 WHERE (job_id = 0xB87B329BFBF7BA40B30D9B27E0B120DE 
 and schedule_id = 8)

Si vous souhaitez exécuter une trace plus filtrée pour suivre ce comportement au fil du temps (par exemple, persister pendant les redémarrages de l'Agent SQL au lieu de sur demande), vous pouvez en exécuter une qui a appname = 'SQLAgent - Schedule Saver'...

Je pense donc que si vous voulez connaître immédiatement la prochaine exécution, regardez sysjobactivity, non sysjobschedules. Ce tableau est directement mis à jour par l'agent ou ses threads d'arrière-plan ("Mettre à jour l'activité de travail", "Gestionnaire de travaux" et "Moteur d'invocation de travaux") au fur et à mesure que l'activité se produit ou comme il est notifié par xp_sqlagent_notify.

Sachez cependant qu'il est très facile de nettoyer les deux tables, car il n'y a aucune protection contre la suppression de données de ces tables. (Ainsi, si vous avez décidé de nettoyer, par exemple, vous pouvez facilement supprimer toutes les lignes de ce travail de la table d'activité.) Dans ce cas, je ne sais pas exactement comment l'Agent SQL Server obtient ou enregistre la prochaine date d'exécution. Peut-être digne d'une enquête plus approfondie à une date ultérieure lorsque j'aurai du temps libre ...

Aaron Bertrand
la source
Si un travail ne s'est pas encore exécuté pour la première fois, sysjobschedules affichera (éventuellement) les valeurs correctes pour next_run_date et next_run_time, tandis que sysjobactivity.next_scheduled_run_date reste null jusqu'à la première exécution. Lorsque vous obtenez la valeur de sysjobactivity, vous devez le faire dans une sous-requête qui regroupe par job_id et obtient le MAX (next_scheduled_run_date).
Mark Freeman
0

msdb.dbo.sp_help_jobsemble toujours renvoyer le correct next_run_date/ next_run_time.

Il utilise sp_get_composite_job_info, qui effectue l'appel suivant pour récupérer réellement le fichier next_run_date/time.

      IF ((@@microsoftversion / 0x01000000) >= 8) -- SQL Server 8.0 or greater
        INSERT INTO @xp_results
        EXECUTE master.dbo.xp_sqlagent_enum_jobs @can_see_all_running_jobs, @job_owner, @job_id
      ELSE
        INSERT INTO @xp_results
        EXECUTE master.dbo.xp_sqlagent_enum_jobs @can_see_all_running_jobs, @job_owner

Puisqu'il ne sysjobschedulesemble pas fiable, je voudrais simplement l'utiliser sp_help_job.

curlyhairedgenius
la source