Как и когда агент SQL обновляет значения next_run_date / next_run_time?

13

Я работал над кодом в T-SQL, чтобы добавить новые расписания в задание агента SQL с помощью процедуры sp_add_jobschedule в базе данных msdb. Когда я добавляю новое расписание (как правило, запускается один раз в определенную дату / время) и сразу же проверяю значения в sysjobschedules и sysschedules, я вижу, что новое расписание было добавлено и привязано к job_id для моего агента SQL работа. Однако значения для next_run_date и next_run_time содержат 0. Когда я возвращаюсь и смотрю на них снова через 2 или 3 минуты, они все еще показывают 0 в них. Однако, когда я возвращаюсь еще через 5 или 10 минут, он теперь правильно показывает значения даты и времени, соответствующие следующему запланированному запуску.

Итак, мои вопросы:

  • Как часто эти значения обновляются?
  • Какой процесс обновляет эти значения?
  • Если бы я должен был добавить расписание, скажем, через 1 минуту в будущем, означает ли это, что задание не будет запущено, поскольку next_run_date / time еще не обновлено?

Пример кода, который я использую для добавления нового расписания:

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

где @jobID - это двоичный файл (16), который содержит job_id рассматриваемого задания, @ScheduleRunDate и @ScheduleRunTime - INT с датой и временем соответственно.

BBlake
источник
3
Он обновляется через задание агента SQL. Который обновляется через задание агента SQL. Рекурсисты объединяются!
Аарон Бертран
1
Извиняюсь. У меня не было возможности вернуться назад.
BBlake

Ответы:

16

Короткий ответ

Похоже, что данные в msdb.dbo.sysjobschedulesобновляются фоновым потоком в агенте SQL, который идентифицируется как SQLAgent - Schedule Saverкаждые 20 минут (или реже, если xp_sqlagent_notifyон не был вызван и в это время не выполнялись никакие задания).

Для более точной информации, посмотрите next_scheduled_run_dateв msdb.dbo.sysjobactivity. Он обновляется в режиме реального времени при каждом изменении задания или выполнении задания. В качестве дополнительного бонуса sysjobactivityданные хранятся правильно (в виде столбца даты и времени), что делает работу с ними намного проще, чем с этими глупыми INT.

Вот краткий ответ:

Это может занять до 20 минут, прежде чем sysjobschedules отражает правду; однако, sysjobactivity всегда будет в курсе. Если вы хотите больше подробностей об этом, или как я понял это ...


Длинный ответ

Если вам захочется проследить за кроликом на мгновение, когда вы позвоните sp_add_jobschedule, эта цепочка событий запускается:

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

Теперь, мы не можем преследовать кролика дальше, потому что мы не можем действительно заглянуть в то, что xp_sqlagent_notifyделает. Но я думаю, что мы можем предположить, что эта расширенная процедура взаимодействует со службой агента и сообщает, что в этой конкретной работе и расписании произошли изменения. Запустив трассировку на стороне сервера, мы увидим, что агент SQL немедленно вызывает следующий динамический 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'

Кажется, что sysjobactivityон обновляется немедленно и sysjobschedulesобновляется только по расписанию. Если мы изменим новое расписание на один раз в день, например

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

Мы по-прежнему видим немедленное обновление, sysjobactivityкак указано выше, а затем еще одно обновление после завершения работы. Различные обновления происходят из фона и других потоков в агенте SQL, например:

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

Фоновый поток (поток «Schedule Saver») в конце концов появляется и обновляется sysjobschedules; из моего первоначального исследования выясняется, что это происходит каждые 20 минут и происходит только в том случае, если xp_sqlagent_notifyон был вызван из-за изменения, внесенного в задание с момента его последнего запуска (я не проводил никаких дальнейших проверок, чтобы увидеть, что произойдет, если одно задание было изменился, и другой был запущен, если поток «Schedule Saver» обновляет оба - я подозреваю, что должен, но оставит это как упражнение для читателя).

Я не уверен, смещен ли 20-минутный цикл с момента запуска агента SQL, или с полуночи, или с чего-то конкретного компьютера. В двух разных экземплярах на одном физическом сервере поток «Расписание по расписанию» обновлялся sysjobschedulesв обоих случаях практически в одно и то же время - 18:31:37 & 18:51:37 на одном и 18:31:39 & 18:51:39 с другой. Я не запускал агент SQL Server одновременно на этих серверах, но существует вероятность того, что время запуска смещено на 20 минут. Я сомневаюсь в этом, но у меня нет времени, чтобы подтвердить это, перезапустив Агент на одном из них и ожидая, пока не произойдет больше обновлений.

Я знаю, кто это сделал, и когда это произошло, потому что я поместил там триггер и захватил его, на случай, если я не смог найти его в трассировке или я случайно отфильтровал его.

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

Тем не менее, это не трудно поймать со стандартной трассировкой, этот даже выглядит как нединамический DML:

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

Если вы хотите запустить более отфильтрованную трассировку, чтобы отслеживать это поведение с течением времени (например, сохранение через перезапуск агента SQL вместо запроса по требованию), вы можете запустить тот, который имеет appname = 'SQLAgent - Schedule Saver'...

Поэтому я думаю, что если вы хотите узнать время следующего запуска сразу, посмотрите sysjobactivity, нет sysjobschedules. Эта таблица напрямую обновляется Агентом или его фоновыми потоками («Активность обновления задания», «Диспетчер заданий» и «Механизм вызова задания») по мере того, как происходит действие или когда оно получает уведомление xp_sqlagent_notify.

Имейте в виду, однако, что очень легко испортить любую из таблиц - поскольку нет защиты от удаления данных из этих таблиц. (Поэтому, если вы решили очистить, например, вы можете легко удалить все строки для этого задания из таблицы действий.) В этом случае я не совсем уверен, каким образом агент SQL Server получает или сохраняет следующую дату выполнения. Возможно, стоит провести дополнительное расследование позже, когда у меня будет немного свободного времени ...

Аарон Бертран
источник
Если задание еще не запущено в первый раз, sysjobschedules (в конце концов) покажет правильные значения для next_run_date и next_run_time, тогда как sysjobactivity.next_scheduled_run_date останется нулевым, пока после первого выполнения. При получении значения из sysjobactivity вам нужно сделать это в подзапросе, который группирует по job_id и получает MAX (next_scheduled_run_date).
Марк Фриман
0

msdb.dbo.sp_help_jobвсегда появляется, чтобы вернуть правильный факт next_run_date/ next_run_time.

Он использует sp_get_composite_job_info, что делает следующий вызов для фактического получения 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

Так как sysjobscheduleкажется ненадежным, я бы просто использовал sp_help_job.

curlyhairedgenius
источник