Tenho trabalhado no código em T-SQL para adicionar novos agendamentos a um trabalho do SQL Agent usando o proc sp_add_jobschedule no banco de dados msdb. Quando adiciono um novo agendamento (normalmente uma execução única em uma data/hora específica) e imediatamente vejo os valores em sysjobschedules e sysschedules, posso ver que o novo agendamento foi adicionado e está vinculado ao job_id para 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 0 neles. No entanto, quando volto 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 é esse que atualiza esses valores?
- Se eu adicionasse um agendamento que fosse, digamos, 1 minuto no futuro, isso significa que o trabalho não será executado, pois next_run_date/time ainda não foi atualizado?
Exemplo do código que uso para adicionar um novo agendamento:
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 é binary(16) que contém o job_id do trabalho em questão, @ScheduleRunDate e @ScheduleRunTime são INTs com a data e hora respectivamente.
Resposta curta
Parece que os dados
msdb.dbo.sysjobschedules
são atualizados por um thread em segundo plano no SQL Agent, identificado comoSQLAgent - Schedule Saver
, a cada 20 minutos (ou com menos frequência, sexp_sqlagent_notify
não tiver sido chamado e nenhum trabalho for executado nesse meio tempo).Para obter informações mais precisas, consulte
next_scheduled_run_date
emmsdb.dbo.sysjobactivity
. Isso é atualizado em tempo real sempre que um trabalho é alterado ou executado. Como um bônus adicional, osysjobactivity
armazena os dados da maneira correta (como uma coluna de data e hora), tornando muito mais fácil trabalhar com eles do que aqueles INTs estúpidos.Essa é a resposta curta:
Resposta longa
Se você quiser seguir o coelho por um momento, ao chamar
sp_add_jobschedule
, esta cadeia de eventos é acionada:Agora, não podemos mais perseguir o coelho, porque não podemos realmente espiar o que
xp_sqlagent_notify
ele faz. 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 seguinte SQL dinâmico é chamado pelo SQL Agent:Parece que
sysjobactivity
é atualizado imediatamente esysjobschedules
só é atualizado em uma programação. Se alterarmos a nova programação para uma vez por dia, por exemploAinda vemos a atualização imediata
sysjobactivity
conforme 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 encadeamentos no SQL Agent, por exemplo:Um thread em segundo plano (o thread "Schedule Saver") eventualmente aparece e atualiza
sysjobschedules
; pela minha investigação inicial, parece que isso ocorre 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 foi executado (não realizei nenhum teste adicional para ver o que acontece se um trabalho foi alterado e outro foi executado, se o tópico "Schedule Saver" atualizar ambos - suspeito que deva, mas deixarei isso como um exercício para o leitor).Não tenho certeza se o ciclo de 20 minutos é compensado quando o SQL Agent é iniciado, ou a partir da meia-noite, ou de algo específico da máquina. Em duas instâncias diferentes no mesmo servidor físico, o thread "Schedule Saver" atualizado
sysjobschedules
, em ambas as instâncias, quase exatamente ao mesmo tempo - 18:31:37 e 18:51:37 em uma e 18:31:39 e 18:51:39 do outro. Não iniciei o SQL Server Agent ao mesmo tempo nesses servidores, mas há uma possibilidade remota de que os horários de início tenham sido de 20 minutos. Duvido, mas não tenho tempo agora para confirmar reiniciando o Agent em um deles e esperando que mais atualizações aconteçam.Eu sei quem fez isso e quando aconteceu, porque coloquei um gatilho ali e capturei, caso não conseguisse encontrá-lo no rastreamento, ou inadvertidamente o filtrasse.
Dito isso, não é difícil pegar com um traço padrão, este até aparece como DML não dinâmico:
Se você deseja executar um rastreamento mais filtrado para rastrear esse comportamento ao longo do tempo (por exemplo, persistir durante as reinicializações do SQL Agent em vez de sob demanda), você pode executar um que tenha appname =
'SQLAgent - Schedule Saver'
...Portanto, acho que, se você quiser saber o próximo tempo de execução imediatamente, consulte
sysjobactivity
, nãosysjobschedules
. Esta tabela é atualizada diretamente pelo Agente ou seus threads de segundo plano ("Atualizar atividade de trabalho", "Gerenciador de trabalho" e "Mecanismo de invocação de trabalho") conforme a atividade ocorre ou é notificada porxp_sqlagent_notify
.Esteja ciente, porém, de que é muito fácil estragar qualquer uma das tabelas - já que 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 digno de mais investigação em uma data posterior, quando eu tiver algum tempo livre...
msdb.dbo.sp_help_job
sempre aparece para retornar onext_run_date
/ real corretonext_run_time
.Ele usa
sp_get_composite_job_info
, que faz a seguinte chamada para realmente recuperar o arquivonext_run_date/time
.Como
sysjobschedule
parece não ser confiável, eu usaria apenassp_help_job
.Eu também queria saber quando next_run_date e next_run_time estão sendo atualizados, mas não consegui encontrá-lo. Se você precisar calcular esses valores sozinho, criei uma UDF que fará exatamente isso.
Código atualizado em 6 de maio de 2022
Consulte https://github.com/paulpeeters/mssql_calculate_next_run_time