Professional Documents
Culture Documents
In this series of posts I am going to take you through how to setup a job control table, and the many benefits
of having a job control table.
At the most basic level, the point of the job control table is to keep a record of every job run, when it started,
when it finished, and whether it was successful.
Here is a sample of the type of data you might have in a control table:
#number and return as Audit_ID. And insert a new record into the SYS_AUDIT_JOB table marking
#the start of the job with current date and time
if($P_JobName is not null)
begin
#Get the unique Audit_id
$L_AuditID = SQL('DATAMART_SQL_TARGET_DS','SELECT max(AUDIT_ID) from SYS_AUDIT_JOB') +
1;
#Insert a new record into the SYS_AUDIT_JOB table
$L_Ret = SQL('DATAMART_SQL_TARGET_DS','INSERT INTO SYS_AUDIT_JOB
(AUDIT_ID,JOB_NAME,JOB_START_DATETIME, JOB_END_DATETIME,STATUS, ETL_VERSION)
VALUES ([$L_AuditID],{$P_JobName},{$P_EffectiveDateAndTime},NULL,\'STARTED\',{$G_ETLVersion})');
print('****************************************************');
print('** ');
print('** Inserted new record into SYS_AUDIT_JOB.');
print('** ');
print('** Return value = {$L_Ret}');
print('** Audit ID = {$L_AuditID}');
print('** ');
print('****************************************************');
#return the unique Audit_Id for this Job
Return $L_AuditID ;
end
Return 0;
As you can see its a pretty straight forward function in that you just pass it you job name, and the current
datetime, and it inserts a row in the SYS_AUDIT_JOB table. Finally it prints out some information to the trace
table. It also returns the audit_id to a variable called $G_AuditID - this will come in handy in part 2.
You may also notice I set the status to STARTED.
Here is how I call the function in the initialise script of my job:
$G_AuditID = NewAuditRecord($G_JobName, $G_EffectiveDateAndTime);
$G_JobName is a global variable and I populate like this:
$G_JobName = job_name();
$G_EffectiveDatAndTime is also a global variable that I populate like this:
$G_EffectiveDateAndTime = sysdate();
Once the job is complete I have another function called EndJob which bascially has this piece of code in it:
print('****************************************************');
print('** ');
print('** Checking for existing jobs.');
print('** ');
#First, find the status of the last job.
$L_LastJobStatus = SQL('DATAMART_SQL_TARGET_DS','SELECT STATUS FROM SYS_AUDIT_JOB
WHERE AUDIT_ID = (SELECT MAX(AUDIT_ID) FROM SYS_AUDIT_JOB where JOB_NAME =
{job_name()})');
print('** Last job status: {$L_LastJobStatus}.');
$L_LastEndTime = SQL('DATAMART_SQL_TARGET_DS','SELECT JOB_END_DATETIME FROM
SYS_AUDIT_JOB WHERE AUDIT_ID = (SELECT MAX(AUDIT_ID) FROM SYS_AUDIT_JOB where
JOB_NAME = {job_name()})');
print('** Last end time: {$L_LastEndTime}.');
#We assume that if there is an end time then the job has finished.
if($L_LastEndTime IS NULL)
begin
print('** Running job found. ');
print('** ');
print('****************************************************');
return 1;
end
else
begin
print('** Last job ended: {$L_LastEndTime}.');
print('** No running jobs found. ');
print('** ');
print('****************************************************');
return 0;
end
I then wrap all my jobs in a conditional which calls this function.
If the function returns 0 there are no other jobs running and the execution can proceed.
If the function finds another job running it returns a 1 and the Conditional executes the Else. In my jobs I
create a simple script that just returns some basic information to the trace file.
Eg.
print('****************************************************');
print('** ');
print('** Another ' || Job_Name() || ' job appears to already be running - this job must be aborted to prevent
corruption or inconsistencies.');
print('** ');
print('****************************************************');
I put this in as standard in all jobs I create as it has saved me a few times. It's also handy when the client
decides that nightly loads aren't enough and they want multiple loads during the day; you can just increase
the load frequency and not worry about the loads crashing into one another.
print('** ');
print('****************************************************');
So prior to executing the job we have an orphaned row in SYS_AUDIT_JOB that looks like this:
Now you can run you job without IsAnotherJobRunning thinking you already have a job running. Also, this
allows you to keep track of how often DS is failing badly and just dying on you without executing the try/catch
blocks.
3. Adding a timeout to your IsAnotherJob function so that it only looks back a certain amount of time. So lets
say your job only ever runs for 30 mins, so you set a timeout for 1 hour. So if you are trying to run a job 1
hour after the last run, and the last run failed without updating the SYS_AUDIT_JOB table, then the next run
will start regardless. This method can be risky though, as your job may legitimately be taking longer than 1 hr
to run and you end up clashing with it by mistake. So only consider this option if you are sure your job will
never take longer than a certain amount of time to run.
http://bodataservices.blogspot.in/2013/04/job-control-table-part-3-preventing_18.html