Seit Oracle 10g Release 2 unterstützt der interne Taskmanager der Datenbank, der sogenannte Scheduler auch komplexe Jobketten. Dies ermöglicht parallele Ausführung von Aufgaben und bedingte Ausführung von Jobs ohne Hilfsmittel wir Cronjobs oder externe Tools. Die folgende Grafik zeigt eine mögliche Jobkette die wir in der Datenbank abbilden werden.
Die Tabelle step_actions dient uns als Protokolltabelle in die jeder Schritt der Jobkette einen Eintrag mit Zeitstempel schreibt. Hierdurch können wir die korrekte Reihenfolge der Steps und die Parallelisierung kontrollieren.
CREATE TABLE step_actions ( STEPNAME VARCHAR2(250) NOT NULL, STEPTIME TIMESTAMP NOT NULL );
Zunächst benötigen wir eine neue Jobkette, zu englisch “Chain”.
begin
DBMS_SCHEDULER.CREATE_CHAIN (
chain_name => 'mytestchain'
);
end;
/
Aus Faulheit erzeuge ich nun die 5 Programmteile in einer Schleife, wobei jedes Programm einen INSERT in die o.g. Tabelle einfügt und anschließend 10 Sekunden wartet. Zudem wird für jedes Programm ein Step in der Jobkette definiert.
declare
step VARCHAR2(250);
prgname VARCHAR2(250);
code VARCHAR2(32767);
begin
FOR i IN 1 .. 5 LOOP
step := 'step' || TO_CHAR(i);
prgname := step || '_prg';
code := '
begin
insert into step_actions values ( ''' || step || ''', SYSTIMESTAMP );
DBMS_LOCK.SLEEP ( 10 );
end;
';
DBMS_SCHEDULER.CREATE_PROGRAM (
program_name => prgname,
program_type => 'PLSQL_BLOCK',
program_action => code,
enabled => TRUE);
DBMS_SCHEDULER.DEFINE_CHAIN_STEP (
chain_name => 'mytestchain',
step_name => step,
program_name => prgname);
END LOOP;
end;
/
Das Herzstück der Jobkette sind die Regeln über die alle Steps miteinander verbunden werden. Die Condition TRUE in der ersten Regel ist unser Einstieg und somit immer wahr. Die Action zeigt, dass unsere Jobkette mit STEP1 startet. In den folgenden Regeln sind die jeweils vorgelagerten Steps als Condition aufgeführt, d.h. die nachfolgenden Steps starten nur nach erfolgreicher Ausführung des Vorgängers. Der Step5 ist natürlich von 2 Steps abhängig währenddessen 2 und 3 direkt nach 1, also parallel ablaufen können. Die Action END bezeichnet das erfolgreiche Ende unserer Jobkette.
begin
DBMS_SCHEDULER.DEFINE_CHAIN_RULE (
chain_name => 'mytestchain',
condition => 'TRUE',
action => 'START step1',
rule_name => 'rule1',
comments => 'Start der Jobkette'
);
DBMS_SCHEDULER.DEFINE_CHAIN_RULE (
chain_name => 'mytestchain',
condition => 'step1 COMPLETED',
action => 'START step2',
rule_name => 'rule2',
comments => 'Start step2'
);
DBMS_SCHEDULER.DEFINE_CHAIN_RULE (
chain_name => 'mytestchain',
condition => 'step1 COMPLETED',
action => 'START step3',
rule_name => 'rule3',
comments => 'Start step3'
);
DBMS_SCHEDULER.DEFINE_CHAIN_RULE (
chain_name => 'mytestchain',
condition => 'step3 COMPLETED',
action => 'START step4',
rule_name => 'rule4',
comments => 'Start step4'
);
DBMS_SCHEDULER.DEFINE_CHAIN_RULE (
chain_name => 'mytestchain',
condition => 'step2 COMPLETED AND step4 COMPLETED',
action => 'START step5',
rule_name => 'rule5',
comments => 'Start step5'
);
DBMS_SCHEDULER.DEFINE_CHAIN_RULE (
chain_name => 'mytestchain',
condition => 'step5 COMPLETED',
action => 'END',
rule_name => 'endrule',
comments => 'Ende der Jobkette'
);
end;
/
Nachdem alle Steps und Regeln definiert wurden muss die Jobkette mit exec DBMS_SCHEDULER.ENABLE( name => ‘mytestchain’ ); aktiviert werden. Um sie letztendlich auszuführen kann nun wie gewohnt ein Job mit einer regelmäßigen Ausführungszeit angelegt werden. Ich führe die Kette hiermit nur exakt einmal aus:
begin
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'mytestchain_job',
job_type => 'CHAIN',
job_action => 'mytestchain',
enabled => TRUE
);
end;
/
Unsere Tabelle step_actions sieht anschließend wie folgt aus. Man sieht schön die 10 Sekunden Dauer der einzelnen Steps. Auch dass Step 2 und 3 parallel ausgeführt wurden und dass Step 5 erst nach Step 4 gestartet ist.
STEPNAME STEPTIME
--------- -----------------
step1 04.03.09 10:39:52
step2 04.03.09 10:40:02
step3 04.03.09 10:40:02
step4 04.03.09 10:40:12
step5 04.03.09 10:40:22
Die Datadictionary View USER_SCHEDULER_JOB_LOG liefert detailierte Informationen über die Ausführung des Jobs:
SELECT LOG_DATE,
JOB_SUBNAME,
OPERATION,
STATUS
FROM USER_SCHEDULER_JOB_LOG
ORDER BY LOG_DATE;
LOG_DATE JOB_SUBNAME OPERATION STATUS
----------------- ------------ ----------- ---------
04.03.09 10:39:51 CHAIN_START RUNNING
04.03.09 10:40:02 STEP1 RUN SUCCEEDED
04.03.09 10:40:12 STEP2 RUN SUCCEEDED
04.03.09 10:40:12 STEP3 RUN SUCCEEDED
04.03.09 10:40:22 STEP4 RUN SUCCEEDED
04.03.09 10:40:32 STEP5 RUN SUCCEEDED
04.03.09 10:40:32 CHAIN_RUN SUCCEEDED
Schlagworte: 10g, DBMS_LOCK, DBMS_SCHEDULER, jobs, Oracle, PL/SQL