Monday, August 22, 2011

Returning Status Results When Calling SQL Server Agent Jobs

One issue with kicking off jobs using the msdb procedure sp_start_job, is that it simply returns a result on whether the job has started successfully or failed to start. It doesn’t return the status on what the job executed. If there’s a failure in the job, you’re blind on the result. We recently have requirement where a front end java workflow process will call a SQL Agent job, and upon completion move on to the next java task. If a failure occurs we need java to log the error. In order to meet this need I wrote a stored procedure that encapsulates the job, and returns a success or failure return value. In the event of a failure it spits out the error message for java to consume. A bullet point of this procedure is:

  • Grab greatest Instance_ID for the job in sysjobhistory table. This lets the procedure know when the job has finished because it will be greater than the last run upon completion
  • A delay on the loop is provided for the calling application to determine how often to check the table the default is 500 ms
  • If the job gets stuck and/or runs too long the calling app can pass in a timeout value to exit and kill the job
  • \ In the event of failure lookup the last step to that failed and report that to the application.

Procedure Code:

USE [msdb]

GO

create procedure [dbo].[JobRun]

@Jobname varchar(500),

@LoopWait varchar(20) = '00:00:01',

@TimeLimit int = 600

as




Declare @historyId int

Declare @CurrentId int

Declare @ErrorMessage nvarchar(1024)

Declare @Jobid varchar(100)

Declare @Starttime datetime

Declare @RunningTime int




--Get starttime to determine if job is running too long

select @Starttime = getdate()



--Get JobId

select @Jobid = job_id from msdb.dbo.sysjobs

where name = @jobname



--Get instance_id as starting point

select @historyid = max(instance_id)

from msdb.dbo.sysjobs j

inner join msdb.dbo.sysjobhistory h

on j.job_id = h.job_id

where J.job_id = @jobid


--Set Baseline For Loop

set @CurrentId = @historyid


--Start Job

EXEC msdb.dbo.sp_start_job @Jobname


--Loop to determine if Job is complete

While @historyid = @CurrentId

Begin

--Checks completion

select @CurrentId = max(instance_id) from msdb.dbo.sysjobs j

inner join msdb.dbo.sysjobhistory h

on j.job_id = h.job_id

where J.name = @jobname

and step_id = 0


--Determine if Job has been running too long

select @RunningTime = datediff(ss, @starttime, getdate())

if @RunningTime > @TimeLimit

Begin

set @ErrorMessage = 'Job Ran for ' + convert(varchar(10), @RunningTime) + ' which is longer then the default baseline value of ' + convert(varchar(10),@TimeLimit)

Goto ExitJobRanToLong

End


-- Wait

waitfor delay @LoopWait

end


--If Job is successful Quit Reporting Success

if exists(

select * from msdb.dbo.sysjobhistory

where instance_id = @CurrentId

and run_status = 1)

Begin

goto ExitProcSuccess

end



--If Job is Failure go to Fail Tag

if exists(

select * from msdb.dbo.sysjobhistory

where instance_id = @CurrentId

and run_status = 0)

Begin

goto ExitProcFailure

end




ExitJobRanToLong:

Raiserror (@ErrorMessage, 16, 1)

--Kill Job

EXEC msdb.dbo.sp_stop_job @Jobname

Return (1)



ExitProcFailure:

--Find Failure Step and pass back error message

select top 1 @ErrorMessage = [message] from msdb.dbo.sysjobhistory

where job_id = @jobid

and step_id != 0

and run_status = 0

order by instance_id desc

--Raiserror with failure message

Raiserror (@ErrorMessage, 16, 1)

Return (1)


--Success Return 0

ExitProcSuccess:

RETURN (0)




No comments: