Jobketten in der Datenbank

jobketteSeit 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: , , , , ,

Kommentieren