Jak i kiedy SQL Agent aktualizuje wartości next_run_date / next_run_time?

13

Pracowałem nad kodem w T-SQL, aby dodać nowe harmonogramy do zadania agenta SQL przy użyciu sp_add_jobschedule proc w bazie danych msdb. Po dodaniu nowego harmonogramu (zwykle jednorazowego uruchomienia o określonej dacie / godzinie) i natychmiastowym przejrzeniu wartości w sysjobschedules i sysschedules, widzę, że nowy harmonogram został dodany i jest powiązany z id_zadania dla mojego agenta SQL praca. Jednak wartości dla next_run_date i next_run_time mają w nich 0. Kiedy wrócę i popatrzę na nich ponownie za 2 lub 3 minuty, nadal pokazują 0 w nich. Jednak gdy wrócę jeszcze 5 lub 10 minut później, teraz poprawnie pokazuje wartości daty i godziny odpowiadające następnemu zaplanowanemu uruchomieniu.

Więc moje pytania to:

  • Jak często te wartości są aktualizowane?
  • Jaki to proces aktualizuje te wartości?
  • Gdybym miał dodać harmonogram, który miałby na przykład 1 minutę w przyszłości, czy to oznacza, że ​​zadanie nie uruchomi się, ponieważ data / data następnej_czasu / czas nie zostały jeszcze zaktualizowane?

Przykład kodu, którego używam do dodania nowego harmonogramu:

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

gdzie @jobID jest binarny (16), który przechowuje id_zadania dla danego zadania, @ScheduleRunDate i @ScheduleRunTime są odpowiednio INT z datą i godziną.

BBlake
źródło
3
Jest aktualizowany za pomocą zadania SQL Agent. Który jest aktualizowany za pomocą zadania SQL Agent. Rekursjoniści jednoczą się!
Aaron Bertrand
1
Przeprosiny. Od jakiegoś czasu nie miałem okazji się wycofać.
BBlake,

Odpowiedzi:

16

Krótka odpowiedź

Wygląda na to, że dane w msdb.dbo.sysjobschedulessą aktualizowane przez wątek w tle w SQL Agent, identyfikowany jako SQLAgent - Schedule Saverco 20 minut (lub rzadziej, jeśli xp_sqlagent_notifynie został wywołany i w międzyczasie nie uruchomiono żadnych zadań).

W celu uzyskania bardziej dokładnych informacji, spójrz na next_scheduled_run_dateIN msdb.dbo.sysjobactivity. Jest on aktualizowany w czasie rzeczywistym za każdym razem, gdy zadanie zostanie zmienione lub zadanie zostanie uruchomione. Jako dodatkowy bonus, sysjobactivityprzechowuje dane we właściwy sposób (jako kolumnę daty i godziny), co znacznie ułatwia pracę z tymi głupimi INT.

Oto krótka odpowiedź:

Może potrwać do 20 minut, zanim sysjobschedules odzwierciedla prawdę; jednak aktywność sysjob będzie zawsze aktualna. Jeśli chcesz o wiele więcej szczegółów na ten temat lub jak to wymyśliłem ...


Długa odpowiedź

Jeśli chcesz przez chwilę podążać za królikiem, kiedy zadzwonisz sp_add_jobschedule, ten łańcuch wydarzeń zostanie uruchomiony:

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

Teraz nie możemy już gonić królika, ponieważ nie możemy tak naprawdę zajrzeć do tego, co xp_sqlagent_notifyrobi. Ale myślę, że możemy założyć, że ta rozszerzona procedura współdziała z usługą agenta i informuje ją, że nastąpiła zmiana w tym konkretnym zadaniu i harmonogramie. Po uruchomieniu śledzenia po stronie serwera możemy natychmiast zobaczyć, że następujący dynamiczny SQL jest wywoływany przez agenta 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'

Wygląda na sysjobactivityto, że jest aktualizowany natychmiast i sysjobschedulesjest aktualizowany tylko zgodnie z harmonogramem. Jeśli zmienimy nowy harmonogram na raz dziennie, np

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

Nadal widzimy natychmiastową aktualizację do sysjobactivitypowyższej, a następnie kolejną aktualizację po zakończeniu zadania. Różne aktualizacje pochodzą z tła i innych wątków w SQL Agent, np .:

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

W końcu pojawia się wątek (wątek „Schedule Saver”) i aktualizuje się sysjobschedules; z mojego wstępnego dochodzenia wydaje się, że odbywa się to co 20 minut i dzieje się tak tylko wtedy, gdy xp_sqlagent_notifyzostał wywołany ze względu na zmianę wprowadzoną w zadaniu od ostatniego uruchomienia (nie wykonałem żadnych dalszych testów, aby zobaczyć, co się stanie, jeśli jedno zadanie zostało wykonane zmieniono i uruchomiono inny, jeśli wątek „Schedule Saver” zaktualizuje oba - podejrzewam, że musi, ale pozostawi to jako ćwiczenie czytelnikowi).

Nie jestem pewien, czy 20-minutowy cykl jest przesunięty w stosunku do momentu uruchomienia agenta SQL, czy od północy, czy od czegoś specyficznego dla maszyny. W dwóch różnych instancjach na tym samym serwerze fizycznym wątek „Schedule Saver” został zaktualizowany sysjobschedules, w obu instancjach, prawie dokładnie w tym samym czasie - 18:31:37 i 18:51:37 na jednym oraz 18:31:39 & 18:51:39 z drugiej strony. Nie uruchomiłem agenta programu SQL Server w tym samym czasie na tych serwerach, ale istnieje zdalna możliwość, że czasy rozpoczęcia były przesunięte o 20 minut. Wątpię, ale nie mam teraz czasu na potwierdzenie, uruchamiając ponownie Agenta na jednym z nich i czekając na kolejne aktualizacje.

Wiem, kto to zrobił, i kiedy to się stało, ponieważ umieściłem tam spust i złapałem go, na wypadek, gdy nie mogłem go znaleźć w śladzie lub przypadkowo go odfiltrowałem.

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

To powiedziawszy, nie jest trudno złapać za pomocą standardowego śledzenia, ten pojawia się nawet jako niedynamiczny DML:

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

Jeśli chcesz uruchomić bardziej przefiltrowane dane śledzenia w celu śledzenia tego zachowania w czasie (np. Utrzymywanie się przez SQL Agent uruchamia się ponownie zamiast na żądanie), możesz uruchomić taki, który ma appname = 'SQLAgent - Schedule Saver'...

Myślę więc, że jeśli chcesz natychmiast poznać następny czas wykonywania, spójrz na sysjobactivitynie sysjobschedules. Ta tabela jest aktualizowana bezpośrednio przez Agenta lub jego wątki w tle („Aktualizuj aktywność zadania”, „Menedżer zadań” i „Mechanizm wywoływania zadań”) w miarę występowania aktywności lub powiadamiania przez nią xp_sqlagent_notify.

Pamiętaj jednak, że bardzo łatwo jest zrównać obie tabele - ponieważ nie ma żadnych zabezpieczeń przed usunięciem danych z tych tabel. (Jeśli na przykład zdecydujesz się wyczyścić, możesz łatwo usunąć wszystkie wiersze dla tego zadania z tabeli działań.) W tym przypadku nie jestem do końca pewien, w jaki sposób SQL Server Agent pobiera lub zapisuje datę następnego uruchomienia. Być może warte dalszych badań w późniejszym terminie, kiedy będę miał trochę wolnego czasu ...

Aaron Bertrand
źródło
Jeśli zadanie nie zostało jeszcze uruchomione po raz pierwszy, sysjobschedules pokaże (ostatecznie) prawidłowe wartości dla next_run_date i next_run_time, podczas gdy sysjobactivity.next_scheduled_run_date pozostaje zerowy aż do pierwszego wykonania. Podczas uzyskiwania wartości z sysjobactivity należy to zrobić w podzapytaniu grupującym według job_id i otrzymującym MAX (next_scheduled_run_date).
Mark Freeman
0

msdb.dbo.sp_help_jobzawsze wydaje się zwracać poprawne rzeczywiste next_run_date/ next_run_time.

Używa sp_get_composite_job_info, który wykonuje następujące wywołanie, aby faktycznie pobrać 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

Ponieważ sysjobschedulewydaje się niewiarygodne, po prostu skorzystam sp_help_job.

curlyhairedgenius
źródło