2011-05-02 396 views
9

Actualmente estoy usando un código como este para detectar si se está ejecutando un trabajo de SQL Server. (Esto es SQL Server 2005, todos de SP)¿Cómo detectar con precisión si un trabajo de SQL Server se está ejecutando y lidiar con el trabajo que ya se está ejecutando?

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) 
) 

No hay problema, y ​​en términos generales, funciona muy bien.

Pero .... (siempre hay un pero)

De vez en cuando, voy a llamar a esta, vuelva un "trabajo no se está ejecutando" número, momento en el que voy a tratar de iniciar el trabajo, a través de

exec msdb.dbo.sp_start_job @JobName 

y SQL retornará que "SQLAgent se ha negado a iniciar el trabajo porque ya tiene una solicitud pendiente".

Ok. Tampoco es un problema. Es concebible que haya una pequeña ventana donde el trabajo objetivo podría comenzar antes de que este código pueda iniciarlo, pero luego de verificar si se inició. Sin embargo, puedo terminar eso en un try catch y simplemente ignorar el error, ¿verdad?

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 

aquí está el problema, sin embargo.

9 veces de cada 10, esto funciona bien. El agente SQL generará un error, se detectará y el procesamiento continuará, ya que el trabajo ya se está ejecutando, no hay daño ni falta.

Pero de vez en cuando, aparece un mensaje en la vista Historial de trabajos (recuerde el código anterior para detectar si un trabajo específico se está ejecutando y ejecútelo si no se está ejecutando desde otro trabajo) diciendo que el trabajo falló porque "SQLAgent se ha negado a comenzar el trabajo porque ya tiene una solicitud pendiente".

¡Por supuesto, este es el error exacto que se supone que TRY CATCH debe manejar!

Cuando esto sucede, el trabajo de ejecución simplemente muere, pero no inmediatamente de lo que puedo decir, solo muy cerca. He puesto el registro por todos lados y no hay consistencia. Una vez que falla, estará en el lugar a, la próxima vez en el lugar b. En algunos casos, el Lugar A y el lugar B no tienen nada más que un

select @var = 'message' 

entre ellos. Muy extraño. Básicamente, el trabajo parece ser abandonado sin ceremonias y todo lo que queda por ejecutar en el trabajo es + no + ejecutado en absoluto.

Sin embargo, si elimino el "exec StartJob" (o lo invoco exactamente una vez, cuando SÉ que el trabajo objetivo no se puede ejecutar), todo funciona perfectamente y todo mi procesamiento en el trabajo se ejecuta .

El objetivo de todo esto es iniciar un trabajo como resultado de un desencadenador (entre otras cosas) y, si el trabajo ya se ha iniciado, realmente no hay necesidad de "iniciarlo de nuevo".

¿Alguien alguna vez ha tenido un comportamiento como este con el manejo de trabajos del Agente SQL?

EDIT: El flujo de corriente de control es de esta manera:

  1. Cambio a una tabla (actualización o inserción) ...
  2. incendios de disparo que llama ...
  3. un procedimiento almacenado que llama a ...
  4. sp_start_job cual ...
  5. comienza un trabajo específico que ...
  6. llama a otro procedimiento almacenado (llamado CheckQueue), que ...
  7. realiza alguna procesamiento y ...
  8. cheques varias mesas y en función de sus contenidos podría ...
  9. invocación sp_start_job en otro puesto de trabajo para poner en marcha un segundo, el trabajo simultáneo para procesar el trabajo adicional (este segundo trabajo llama al procedimiento almacenado CheckQueue también pero las dos invocaciones operan en conjuntos de datos completamente separados)
+0

No es la respuesta que realmente estoy buscando, pero como una solución, he creado una tabla de "monitor", que contiene una bandera que indica si cada trabajo en particular es "realmente" en ejecución o no, y el tiempo de expiración hasta proteger contra los fracasos. Por lo tanto, si este indicador indica que un trabajo no se está ejecutando, no hay manera de que se pueda ejecutar, por lo que es seguro iniciarlo. Si indica que se está ejecutando, uso el método sp_help_job para verificar si el trabajo se está ejecutando. si eso devuelve que NO se está ejecutando. Choco con un mostrador y continúo. – DarinH

+0

Si el contador se golpea x veces seguidas con una cantidad de tiempo entre ellas, entonces puedo estar razonablemente seguro de que el trabajo NO se está ejecutando realmente, borrar su estado y ponerlo a disposición para volver a ejecutarlo. Dolor en el trasero, pero funciona de manera consistente. – DarinH

+0

Aún espero una solución un poco menos indirecta, aunque – DarinH

Respuesta

4

Antes que nada, ¿ha tenido la oportunidad de consultar al agente de servicios? Según tu descripción, parece que eso es lo que realmente quieres.

La diferencia sería en lugar de comenzar un trabajo, usted pone sus datos en una cola de SB y SB llamará a su proceso de procesamiento de forma asincrónica y totalmente a un paso con los trabajos que ya están en ejecución, etc. Generará automáticamente/terminará los hilos y la demanda dictan, se encarga del orden, etc.

Aquí hay un buen tutorial (y vagamente relacionado). http://www.sqlteam.com/article/centralized-asynchronous-auditing-with-service-broker

Supongamos que no puede usar SB por el motivo que sea (¡pero en serio, sí!).

¿Qué ocurre con el uso de context_info de spid del trabajo.

  1. Su trabajo llama a un proceso de envoltura que ejecuta cada paso individualmente.
  2. La primera declaración dentro del proc envoltorio es

    DECLARE @context_info VARBINARY(30) 
    SET @context_info = CAST('MyJob1' AS VARBINARY) 
    SET CONTEXT_INFO @context_info 
    
  3. Cuando sus acabados proc (o en su bloque catch)

    SET CONTEXT_INFO 0x0 
    
  4. Cuando usted está mirando llamando a su trabajo, hacen esto :

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

Cuando finaliza su proceso de envoltura o la conexión se cierra, su context_info desaparece.

También podría usar una tabla temporal global (es decir, ## JobStatus) Desaparecerán cuando todos los spids que hacen referencia a ella se desconecten o si se eliminan explícitamente.

Solo algunas consideraciones.

+0

No sabía acerca de la función Context_info. Interesante, aunque, al final del día, ese enfoque es básicamente lo mismo que usar una tabla, con filas que contienen los SPID de los trabajos y cierta información de seguimiento. Posiblemente un poco más simple (ya que la información de contexto desaparece automáticamente cuando finaliza el trabajo. – DarinH

+0

Inicialmente busqué en ServiceBroker, pero el cliente lo negó ya que no tenían a nadie que supiera nada sobre el personal. Sin embargo, las cosas del servicebroker son definitivamente en mi mente para proyectos futuros similares. Gracias! – DarinH

+0

Le doy la vuelta a esta respuesta, principalmente porque 1) menciona CONTEXT_INFO que no había usado antes, y 2) es esencialmente lo que terminé usando un seguimiento de trabajos tabla, aunque la tabla Global Temp podría ser un poco + limpiador, ya que tiende a limpiarse automáticamente, mientras que en una tabla normal, tenía que asegurarme de que las cosas se limpiaran explícitamente. – DarinH

-3

para hacer frente a un puesto de trabajo ya se está ejecutando: 1. Abra el Administrador de tareas 2. Comprobar si un proceso con ImageName "DTExec.exe" se está ejecutando 3. Si el proceso está en marcha y si es el trabajo problemático, ejecutar "Terminar proceso".

+0

No estaba buscando una solución manual. – DarinH

2

Tengo una consulta que me da los trabajos en ejecución, tal vez puede ayudarle. Ha funcionado para mí, pero si encuentra algún error en ello, hágamelo saber, intentaré rectificar. aclamaciones.

-- 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) 
Cuestiones relacionadas