DBMS_SCHEDULER und die Sommerzeit, DB-Job richtig timen

Bei der Weiterentwicklung eines Projektes ist aufgefallen, dass ein DB-Job 1 Stunde zu früh ausgeführt wird.
Geplant für 06:20Uhr wird er bereits um 05:20Uhr ausgeführt:

-- Ausführungshistorie
select to_char(l.log_date,'dd.mm.yyyy hh24:mi:ss.ff TZR') "Log Date TZ"
          , to_char(r.req_start_date,'dd.mm.yyyy hh24:mi:ss.ff TZR') "Required Start Date TZ"
          , to_char(r.actual_start_date,'dd.mm.yyyy hh24:mi:ss.ff TZR') "Actual Start Date TZ"
    from ALL_SCHEDULER_JOB_LOG L, ALL_SCHEDULER_JOB_RUN_DETAILS R
  where l.Owner = 'my'
      and l.job_name = 'myJob'
      and l.log_id = r.log_id (+)
  order by 1 desc;
liefert
Log Date TZ Required Start Date TZ Actual Start Date TZ
31.12.2022 05:21:06.062526 +01:00 31.12.2022 06:20:00.366020 +02:00 31.12.2022 06:20:00.445268 +02:00
30.12.2022 05:22:15.510405 +01:00 30.12.2022 06:20:00.160237 +02:00 30.12.2022 06:20:00.354385 +02:00
29.12.2022 05:21:29.299165 +01:00 29.12.2022 06:20:00.019715 +02:00 29.12.2022 06:20:00.135502 +02:00
Eigentlich ist alles korrekt:
Ein für 06:20Uhr in Zeitzone +02:00 geplanter Start startet in Zeitzone +01:00 eben um 05:20Uhr.
Nur war das anders gedacht!

Erstellt wurde der Job per Script im Sept 2022:

-- DB-Job erstellen - Fehlerhaft!
SYS.DBMS_SCHEDULER.CREATE_JOB
  (
    job_name => 'myJob'
    ,start_date => TO_TIMESTAMP_TZ('20220928','yyyymmdd')
    ,repeat_interval => 'FREQ=DAILY; BYHOUR=6; BYMINUTE=20; BYSECOND=0'
    ,end_date => NULL
    ,job_class => 'DEFAULT_JOB_CLASS'
    ,job_type => 'STORED_PROCEDURE'
    ,job_action => 'myProc'
    ,comments => 'myProc-Kommentar'
    ,enabled => false
  );

Dabei scheint dem START_DATE eine größere Rolle zuzukommen, als vermutet.

Die Prüfung per

-- Ausführung prüfen
select to_char (START_DATE, 'yyyy/mm/dd hh24:mi:ss.ff tzr') START_DATE
          , REPEAT_INTERVAL
          , LAST_START_DATE
          , NEXT_RUN_DATE
    from SYS.ALL_SCHEDULER_JOBS
  where JOB_NAME = 'myJob';
zeigt, das für den Start
28.09.2022 00:00:00,000000 +02:00
verwendet wird.
Und eben dieses +02:00 ist das Problem. Da NEXT_RUN_DATE basierend auf START_DATE berechnet wird, gibt es eine Verschiebung bei der Zeitumstellung. Im September befindet sich die Datenbank in Zeitzone UTC+02:00, weil Sommerzeit. Im Winter liegt sie in UTC+01:00. Da +02:00 hart vorgegeben ist, startet der Job im Winter früher.

Was kann man tun?

Grundsätzlich gibt es für SYS.DBMS_SCHEDULER.CREATE_JOB folgende Optionen:

  1. ,start_date => TO_TIMESTAMP_TZ('20230101','yyyymmdd') --> wie oben, nicht gut!
  2. ,start_date => TO_TIMESTAMP_TZ('20230101 EUROPE/BERLIN','yyyymmdd TZR') --> Zeitzonenregion (TZR) mit angeben -> beste Lösung, da eindeutig!
  3. Parameter start_date weglassen --> dann bestimmt die Datenbank über verschiebende Ebenen den Wert. (Siehe dazu den Blogeintrag von Martin Preiss.)

Im Beispiel ergeben sich für die drei Varianten folgende START_DATE:

Option NEXT_RUN_DATE START_DATE
1 04.01.2023 06:20:00,628493 +01:00 2023/01/01 00:00:00.000000 +01:00
2 04.01.2023 06:20:00,946355 +01:00 2023/01/01 00:00:00.000000 Europe/Berlin
3 04.01.2023 06:20:00,588781 +01:00 2023/01/03 10:59:59.588781 Europe/Vienna
(Nicht verwirren lassen: Jetzt bei Test ist Winterzeit, daher die +01:00).

Fazit: Mit der Umstellung auf DBMS_SCHEDULER ist die Job-Steuerung der Datenbank sehr viel leistungsfähiger geworden. Gleichzeitig gibt es aber auch mehr Punkte zu beachten.

-- DB-Job erstellen, mit korrekter Startzeitvorgabe
SYS.DBMS_SCHEDULER.CREATE_JOB
  (
    job_name => 'myJob'
    ,start_date => TO_TIMESTAMP_TZ('20230101 EUROPE/BERLIN','yyyymmdd TZR')
    ,repeat_interval => 'FREQ=DAILY; BYHOUR=6; BYMINUTE=20; BYSECOND=0'
    ,end_date => NULL
    ,job_class => 'DEFAULT_JOB_CLASS'
    ,job_type => 'STORED_PROCEDURE'
    ,job_action => 'myProc'
    ,comments => 'myProc-Kommentar'
    ,enabled => false
  );

Den bestehende Job umzustellen schaut dann so aus:

-- bestehende Job auf korrekte Startzeitvorgabe umstellen
SYS.DBMS_SCHEDULER.SET_ATTRIBUTE
  (
    name => 'myJob'
    , attribute => 'start_date'
    , value => TO_TIMESTAMP_TZ('20220928 EUROPE/BERLIN','yyyymmdd TZR')
  );
...dann passen auch die Ausführzeiten wieder.

Eine gute Erläuterung zu diesem Thema findet sich im Blog von Martin Preiss:
https://martinpreiss.blogspot.com/2012/03/dbmsscheduler-und-die-sommerzeit.html

Kommentare

Beliebte Posts aus diesem Blog

PGA unter Oracle 11g

trunc(sysdate) - nette Spiele mit dem Datum

Datapump - Verzeichnis erstellen