Como e quando o SQL Agent atualiza os valores next_run_date / next_run_time?

13

Estou trabalhando no código no T-SQL para adicionar novos agendamentos a um trabalho do SQL Agent usando o processo sp_add_jobschedule no banco de dados msdb. Quando adiciono um novo agendamento (normalmente uma vez em uma data / hora específica) e examino imediatamente os valores em sysjobschedules e sysschedules, vejo que o novo agendamento foi adicionado e está vinculado ao job_id do meu SQL Agent trabalho. No entanto, os valores para next_run_date e next_run_time têm 0 neles. Quando volto e olho para eles novamente em 2 ou 3 minutos, eles ainda mostram 0s neles. No entanto, quando volto mais 5 ou 10 minutos depois, ele agora mostra corretamente os valores de data e hora correspondentes à próxima execução agendada.

Então, minhas perguntas são:

  • Com que frequência esses valores são atualizados?
  • Que processo atualiza esses valores?
  • Se eu adicionar uma programação que seja, digamos, 1 minuto no futuro, isso significa que o trabalho não será executado, pois o próximo_run_date / time ainda não foi atualizado?

Exemplo do código que eu uso para adicionar uma nova programação:

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

onde @jobID é binário (16) que contém o job_id do trabalho em questão, @ScheduleRunDate e @ScheduleRunTime são INTs com data e hora, respectivamente.

BBlake
fonte
3
É atualizado por meio de um trabalho do SQL Agent. Que é atualizado através de um trabalho do SQL Agent. Os recursionistas se unem!
Aaron Bertrand
1
Desculpas. Não tive a chance de voltar atrás há um tempo.
BBlake

Respostas:

16

Resposta curta

Parece que os dados msdb.dbo.sysjobschedulessão atualizados por um encadeamento em segundo plano no SQL Agent, identificado comoSQLAgent - Schedule Saver cada 20 minutos (ou menos frequentemente, se xp_sqlagent_notifynão tiver sido chamado e nenhum trabalho tiver sido executado nesse meio tempo).

Para obter informações mais precisas, olhada next_scheduled_run_dateno msdb.dbo.sysjobactivity. Isso é atualizado em tempo real sempre que um trabalho é alterado ou um trabalho é executado. Como um bônus adicional, osysjobactivity armazena os dados da maneira certa (como uma coluna de data e hora), facilitando muito o trabalho com essas INTs estúpidas.

Essa é a resposta curta:

Pode levar até 20 minutos para que os sysjobschedules reflitam a verdade; no entanto, a atividade do sistema sempre estará atualizada. Se você quiser muito mais detalhes sobre isso, ou como eu descobri ...


Resposta longa

Se você quiser seguir o coelho por um momento, quando ligar sp_add_jobschedule, essa cadeia de eventos será acionada:

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

Agora, não podemos mais perseguir o coelho, porque realmente não podemos espiar o que xp_sqlagent_notifyacontece. Mas acho que podemos presumir que esse procedimento estendido interage com o serviço do Agente e informa que houve uma alteração nesse trabalho e cronograma específicos. Ao executar um rastreamento do lado do servidor, podemos ver que, imediatamente, o SQL dinâmico a seguir é chamado pelo SQL Agent:

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'

Parece que sysjobactivityé atualizado imediatamente e sysjobschedulesé atualizado apenas em uma programação. Se mudarmos o novo horário para uma vez por dia, por exemplo,

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

Ainda vemos a atualização imediata sysjobactivityconforme acima e, em seguida, outra atualização após a conclusão do trabalho. Várias atualizações vêm de segundo plano e outros threads no SQL Agent, por exemplo:

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

Um thread em segundo plano (o thread "Schedule Saver") finalmente aparece e é atualizado sysjobschedules; da minha investigação inicial, parece que isso é a cada 20 minutos e só acontece sexp_sqlagent_notify tiver sido chamado devido a uma alteração feita em um trabalho desde a última vez em que ele foi executado (não realizei nenhum teste adicional para ver o que acontece se um trabalho tiver sido executado). mudou e outro foi executado, se o segmento "Schedule Saver" atualizar os dois - eu suspeito que deve, mas deixará isso como um exercício para o leitor).

Não tenho certeza se o ciclo de 20 minutos é deslocado a partir do momento em que o SQL Agent é iniciado, ou da meia-noite ou de algo específico da máquina. Em duas instâncias diferentes no mesmo servidor físico, o segmento "Schedule Saver" foi atualizadosysjobschedules , nas duas instâncias, quase exatamente no mesmo horário - 18:31:37 e 18:51:37 em um e 18:31:39 e 18:51:39 por outro. Não iniciei o SQL Server Agent ao mesmo tempo nesses servidores, mas existe a possibilidade remota de que os horários de início tenham sido 20 minutos de deslocamento. Duvido, mas não tenho tempo no momento para confirmar reiniciando o Agent em um deles e aguardando mais atualizações.

Sei quem o fez e quando aconteceu, porque coloquei um gatilho lá e o capturei, caso não conseguisse encontrá-lo no rastro ou o filtrasse inadvertidamente.

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

Dito isto, não é difícil capturar com um rastreamento padrão, esse mesmo aparece como DML não dinâmico:

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

Se você deseja executar um rastreamento mais filtrado para rastrear esse comportamento ao longo do tempo (por exemplo, persistir na reinicialização do SQL Agent em vez de sob demanda), é possível executar um que tenha appname = 'SQLAgent - Schedule Saver'...

Então, acho que se você quiser saber o próximo tempo de execução imediatamente, olhe sysjobactivity, não sysjobschedules. Esta tabela é diretamente atualizada pelo Agent ou por seus encadeamentos em segundo plano ("Atualizar atividade da tarefa", "Gerenciador de tarefas" e "Mecanismo de chamada de tarefas") à medida que a atividade acontece ou é notificada por xp_sqlagent_notify.

Esteja ciente, no entanto, de que é muito fácil ocultar uma das tabelas - pois não há proteção contra a exclusão de dados dessas tabelas. (Portanto, se você decidir limpar, por exemplo, poderá remover facilmente todas as linhas desse trabalho da tabela de atividades.) Nesse caso, não tenho certeza de como o SQL Server Agent obtém ou salva a próxima data de execução. Talvez seja digno de mais investigação em uma data posterior, quando eu tiver algum tempo livre ...

Aaron Bertrand
fonte
Se um trabalho ainda não foi executado pela primeira vez, os sysjobschedules mostrarão (eventualmente) os valores corretos para next_run_date e next_run_time, enquanto sysjobactivity.next_scheduled_run_date permanecerá nulo até após a primeira execução. Ao obter o valor de sysjobactivity, você precisa fazer isso em uma subconsulta que agrupa por job_id e obtém o MAX (next_scheduled_run_date).
Mark Freeman
0

msdb.dbo.sp_help_jobsempre aparece para retornar o valor real next_run_date/next_run_time .

Ele usa sp_get_composite_job_info, que faz a seguinte chamada para recuperar o 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

Desde que sysjobscheduleparece não ser confiável, eu apenas usaria sp_help_job.

curlyhairedgenius
fonte