Как точно определить, выполняется ли задание SQL Server, и справиться с уже запущенным заданием?

В настоящее время я использую такой код, чтобы определить, выполняется ли задание SQL-сервера. (это SQL Server 2005, все SP)

return (select isnull(  
(select top 1 CASE 
    WHEN current_execution_status = 4 THEN 0
    ELSE 1
    END
from openquery(devtestvm, 'EXEC msdb.dbo.sp_help_job')
where current_execution_status = 4 and
    name = 'WQCheckQueueJob' + cast(@Index as varchar(10))
), 1)
)

Никаких проблем там нет, и вообще говоря, он работает просто отлично.

Но.... (всегда но)

Иногда я вызываю это, получаю результат «задание не выполняется», после чего я пытаюсь запустить задание через

exec msdb.dbo.sp_start_job @JobName

и SQL вернет, что «SQLAgent отказался запускать задание, потому что у него уже есть ожидающий запрос».

В порядке. Тоже не проблема. Вполне возможно, что есть небольшое окно, в котором целевое задание может быть запущено до того, как этот код сможет его запустить, но после проверки того, запущено ли оно. Однако я могу просто обернуть это в try catch и просто проигнорировать ошибку, верно?

begin try
if dbo.WQIsQueueJobActive(@index) = 0 begin
    exec msdb.dbo.sp_start_job @JobName
    break
end         
end try begin catch
    -- nothing here
end catch

вот проблема однако.

В 9 случаях из 10 это работает просто отлично. Агент SQL вызовет ошибку, она будет обнаружена, и обработка продолжится, так как задание уже запущено, без вреда для здоровья.

Но иногда я получаю сообщение в представлении «История заданий» (имейте в виду приведенный выше код, чтобы определить, выполняется ли конкретное задание, и запустить его, если оно на самом деле не выполняется из другого задания), в котором говорится, что задание не выполнено, потому что «SQLAgent отказался запускать задание, потому что оно уже имеет ожидающий запрос».

Конечно, это именно та ошибка, которую TRY CATCH должен обрабатывать!

Когда это происходит, выполняемая работа просто умирает, но не сразу, насколько я могу судить, просто довольно близко. Я поставил регистрацию повсюду, и нет согласованности. Один раз он потерпит неудачу, он будет в месте а, в следующий раз в месте б. В некоторых случаях место А и место Б не имеют ничего, кроме

select @var = 'message'

между ними. Очень странно. По сути, задание выглядит бесцеремонно сброшенным, и все, что осталось выполнить в задании, +не+ выполняется вообще.

Однако, если я удалю «exec StartJob» (или вызову его ровно один раз, когда я ЗНАЮ, что целевое задание уже не может быть запущено), все работает отлично, и вся моя обработка в задании выполняется.

Целью всего этого является запуск задания в результате срабатывания триггера (среди прочего), и, если задание уже запущено, нет необходимости «запускать его снова».

Кто-нибудь когда-либо сталкивался с подобным поведением при обработке заданий агента SQL?

РЕДАКТИРОВАТЬ: Текущий поток управления выглядит так:

  1. Перейти к таблице (обновить или вставить)...
  2. срабатывает триггер, который вызывает...
  3. хранимая процедура, которая вызывает...
  4. sp_Start_Job, который...
  5. запускает определенную работу, которая...
  6. вызывает другой хранимый процесс (называемый CheckQueue), который...
  7. выполняет некоторую обработку и...
  8. проверяет несколько таблиц и в зависимости от их содержимого может...
  9. вызвать sp_start_job для другого задания, чтобы запустить второе одновременное задание для обработки дополнительной работы (это второе задание также вызывает sproc CheckQueue, но два вызова работают с совершенно разными наборами данных)

person DarinH    schedule 02.05.2011    source источник
comment
Не тот ответ, который я действительно ищу, но в качестве обходного пути я создал таблицу мониторинга, которая содержит флаг, указывающий, действительно ли выполняется каждое конкретное задание, и время истечения срока действия для защиты от сбоев. Таким образом, если этот флаг указывает на то, что задание не запущено, оно никак не может быть запущено, поэтому запускать его безопасно. Если он указывает, что он запущен, я использую метод sp_help_job, чтобы проверить, выполняется ли задание. если это возвращает, что он НЕ работает. Я натыкаюсь на счетчик и продолжаю.   -  person DarinH    schedule 27.05.2011
comment
Если счетчик сбивается x раз подряд с промежутком времени между ними, то я могу быть достаточно уверен, что задание на самом деле НЕ выполняется, очистить его статус и сделать его доступным для повторного запуска. Боль в прикладе, но он работает стабильно.   -  person DarinH    schedule 27.05.2011
comment
Все еще надеюсь на немного менее окольное решение, хотя   -  person DarinH    schedule 27.05.2011


Ответы (3)


Во-первых, была ли у вас возможность взглянуть на сервисного брокера? Судя по вашему описанию, это то, что вы действительно хотите.

Разница будет заключаться в том, что вместо запуска задания вы помещаете свои данные в очередь SB, и SB будет вызывать процесс обработки асинхронно и полностью устранять проблемы с уже запущенными заданиями и т. д. Он будет автоматически запускать/завершать дополнительные потоки, а спрос диктует , следит за порядком и т. д.

Вот хороший (и смутно связанный) учебник. http://www.sqlteam.com/article/centralized-asynchronous-auditing-with-service-broker

Предположим, что вы не можете использовать SB по какой-либо причине (но серьезно, используйте!).

Как насчет использования идентификатора задания context_info.

  1. Ваша работа вызывает процедуру-оболочку, которая выполняет каждый шаг отдельно.
  2. Первый оператор внутри процесса-оболочки:

    DECLARE @context_info VARBINARY(30)
    SET @context_info = CAST('MyJob1' AS VARBINARY)
    SET CONTEXT_INFO @context_info
    
  3. Когда ваш процесс завершится (или в вашем блоке catch)

    SET CONTEXT_INFO 0x0
    
  4. Когда вы смотрите на вызов своей работы, сделайте следующее:

    IF NOT EXISTS (SELECT * FROM master..sysprocesses WITH (NOLOCK) WHERE context_info=CAST('MyJob1' AS VARBINARY))
        EXEC StartJob
    

Когда ваш процесс-оболочка завершается или соединение закрывается, ваш context_info исчезает.

Вы также можете использовать глобальную временную таблицу (например, ##JobStatus). Они исчезнут, когда все SPID, которые ссылаются на нее, отключатся или если она явно удалена.

Просто несколько мыслей.

person Code Magician    schedule 03.08.2011
comment
Я не знал о функции Context_info. Интересно, хотя, в конце концов, этот подход в основном аналогичен использованию таблицы со строками, содержащими SPID заданий и некоторую информацию об отслеживании. Возможно, это немного проще (поскольку информация о контексте автоматически исчезает после завершения задания. - person DarinH; 04.08.2011
comment
Сначала я изучил ServiceBroker, но клиент отклонил это, поскольку у них не было никого, кто знал бы об этом в штате. Тем не менее, я определенно думаю о сервис-брокерах для подобных будущих проектов. Спасибо! - person DarinH; 04.08.2011
comment
Я даю этому ответу кивок, главным образом потому, что 1) он упоминает CONTEXT_INFO, который я не использовал раньше, и 2) это, по сути, то, что я закончил с использованием таблицы отслеживания заданий, хотя таблица Global Temp может быть + немного+ чище, поскольку он имеет тенденцию убирать за собой автоматически, тогда как с обычной таблицей мне приходилось следить за тем, чтобы все очищалось явно. - person DarinH; 15.08.2011

У меня есть запрос, который дает мне запущенные задания, может быть, это может вам помочь. У меня это работает, но если вы обнаружите в нем какую-либо ошибку, дайте мне знать, я постараюсь исправить. ваше здоровье.

-- get the running jobs
--marcelo miorelli
-- 10-dec-2013


SELECT sj.name
      ,DATEDIFF(SECOND,aj.start_execution_date,GetDate()) AS Seconds
 FROM msdb..sysjobactivity aj
 JOIN msdb..sysjobs sj on sj.job_id = aj.job_id
WHERE aj.stop_execution_date IS NULL -- job hasn't stopped running
 AND aj.start_execution_date IS NOT NULL -- job is currently running
--AND sj.name = 'JobName'
and not exists( -- make sure this is the most recent run
    select 1
    from msdb..sysjobactivity new
    where new.job_id = aj.job_id
      and new.start_execution_date > aj.start_execution_date )
person Marcello Miorelli    schedule 27.09.2014

Чтобы справиться с уже запущенным заданием: 1. Откройте Диспетчер задач. 2. Проверьте, запущен ли процесс с именем ImageName "DTExec.exe". 3. Если процесс запущен и является проблемным заданием, выполните команду "Завершить процесс".

person Menahem    schedule 10.07.2013