Seit V11.2 hat die ONE Automation ein tolles neues Feature: Dank der Zero Downtime Upgrades können Sie Ihr Automic System Upgraden, ohne dass es dadurch zu Unterbrechungen kommt. Aber ganz ohne Downtime kommen Sie wahrscheinlich trotzdem nicht aus, denn nicht Sie müssen ja nicht nur die Automic Software aktualisieren.

Wenn Sie Software upgraden, die von ONE Automation orchestriert wird, und diese Software dafür gestoppt werden muss, unterbrechen Sie normalerweise auch die zugehörigen Prozesse in der ONE Automation. Zum Beispiel den Client oder die Queue.

Danach, wenn die Software wieder läuft, starten Sie auch den Client oder die Queue wieder. Alle Tasks, deren Start während der Downtime geplant war, bekommen den Status: ENDED_TIMEOUT.

Nach einer solchen Downtime ist es nützlich, eine Liste mit allen Tasks zu haben, die während der Downtime hätten starten sollen.

In diesem Artikel, konzentriere ich mich auf Aufgaben, die im Scheduler geplant waren. Auf Period Containers und Events werde ich nicht eingehen.

Um Tasks zu finden, die für eine bestimmte Zeit gescheduled waren, aber nicht ausgeführt wurden, muss man 3 Schritte durchführen:

  1. Tasks identifizieren, die für den betroffenen Zeitraum geplant waren und mit dem Status ENDED_TIMEOUT endeten
  2. Kontrollieren, ob es Kalender-Bedingungen gibt, die eine Ausführung der Task sowieso verhindert hätten
  3. Die Schritte 1 und 2 verbinden

Legen wir also los.

Nicht gestartete Tasks identifizieren

Als erstes suchen wir alle Tasks, die gescheduled waren, aber nicht gestartet wurden. Dafür brauchen wir die zwei Tabellen EH (Executive Header) und EJPP (Jobplan process – active).

In der Spalte EH_Otype in EH ist der Typ der Aktivität gespeichert. Wir suchen nach per Schedule geplanten Tasks, der EH_Otype muss also JSCH sein.

In der Spalte EJPP_Status in EJPP steht der aktuelle Status des Jobs. Wir suchen nach ENDED_TIMEOUT, was dem Code 1941 entspricht.

Unsere Query sucht also nach Tasks mit dem EJPP_Status 1941 und dem EH_Otype „JSCH“. Von den identifizierten Zeilen brauchen wir den Namen des Clients (EH_Client), den Namen des Schedules (EH_Name) und den Namen der Tasks (EJPP_Object).

Für später brauchen wir außerdem noch die vorgesehene Startzeit der Tasks. Die bekommen wir über EJPP_ErlstStTimeT. Achtung: die Zeitangabe ist in UTC.

Daraus ergibt sich die folgende SQL Query:

SELECT EH_Client, EH_Name, EJPP_Object, EJPP_ErlstStTimet, EJPP_CCType, EJPPC_CaleName, EJPPC_CaleKeyName
from EH
inner join EJPP on EH_AH_IDNR = EJPP_AH_IDNR
left join EJPPC on EH_AH_IDNR = EJPP_AH_IDNR and EJPP_Lnr = EJPPC_EJPP_Lnr
Where EH_Otype = 'JSCH'
And EJPP_Status = 1941
and EJPP_Active = 1
order by EJPP_ErlstStTimeT;

Benötigte Kalenderinformationen

Als nächstes nehmen wir uns die Kalenderbedingungen vor. Wenn ein Task eine Kalenderbedingung hat, wäre er am Tag der Downtime möglicherweise sowieso nicht ausgeführt worden – auch wenn es keine Downtime gegeben hätte. Diese Tasks müssen wir noch herausfiltern.

In der folgenden Abbildung sehen Sie eine Kalenderbedingung. Unter „Execute if“ wurde „no condition matches“ ausgewählt. Der Task wird also nur ausgeführt, wenn weder MONDAY noch STATIC zutreffen.

Der Wert von „Execute if“ steht in der Tabelle EJPP in der Spalte EJPP_CCType. Die möglichen Werte sind:

1 = „all conditions match“

2 = „one condition matches“

3 = „no condition matches

Aus der Tabelle EJPPC (Jobplan calendar condition – active) brauchen wir noch die folgenden Informationen:

EJPPC_EJPP_Lnr Referenz zum Jobplan Process (EJPP)
EJPPC_CaleName Objektname (Calendar)
EJPPC_CaleKeyName Name des Keywords

Kalender-Bedingungen in der Query prüfen

Bis hierher haben wir nur ein paar einfache Informationen abgerufen. Jetzt kommt der spannende Teil. Hier zeigt sich mal wieder, wie nützlich es ist, SQL zu beherrschen.

Wir prüfen jetzt nämlich die Kalender-Bedingungen direkt im SQL. Die Query muss also die Kalenderobjekte aufrufen und prüfen, ob dort das Datum der Downtime enthalten ist.

Den Namen des Kalenders und des Keywords haben wir aus der Tabelle EJPPC ausgelesen. Kalender sind in OH, Keywords in OKB gespeichert.

Wenn Sie einen Kalender bearbeiten, also ein Kalender-Event (=Keyword) hinzufügen oder ändern, und die Änderungen speichern, berechnet ONE Automation die Tage, an denen die Bedingung zutrifft – und zwar für das vergangene Jahr, das aktuelle Jahr und die nächsten 6 Jahre (es sei denn Ihr Admin hat diesen Wert geändert).

Die berechneten Tage werden in Tabelle OKD gespeichert. Für jedes Jahr gibt es eine Tabelle und in der Spalte OKD_Content steht eine lange Kette aus Einsen und Nullen. Jede Ziffer steht für einen Tag des Jahres und gibt an, ob die Bedingung an diesem Tag erfüllt ist oder nicht.

Aus EJPP_ErlstSttimeT wissen wir das Datum, deshalb können wir auf die entsprechende Ziffer in OKD_Content zugreifen. Wir geben diesen binären Wert in der berechneten Spalte „CaleActive“ aus:

...
substring(OKD_Content, 31*(month(EJPP_ErlstSttimeT)-1)+day(EJPP_ErlstSttimeT), 1) as CaleActive
...

Anmerkung: In OKD_Content sind für jeden Monat 31 Stellen reserviert. So kann man das Feld besser in SQL verwenden.

In der folgenden Grafik sehen Sie, auf welche Tabellen wir zugreifen müssen und wie wir sie miteinander verbinden.

In der folgenden Tabelle sehen Sie, wie das Ergebnis einer solchen Abfrage aussehen könnte:

 

EH_Client EH_Name EJPP_Object EJPP_Lnr EJPP_ErlstSttimeT EJPP_CCType EJPPC_CaleName EJPPC_CaleKeyName Cale_Active
1000 JSCH.001 JOBS.001 1 2016-11-02 12:00:00 NULL NULL NULL NULL
1000 JSCH.001 JOBS.002 2 2016-11-02 14:00:00 3 (=No) Cale.001 MONDAY 0
1000 JSCH.001 JOBS.002 2 2016-11-02 14:00:00 3 (=No) Cale.001 STATIC  1
1000 JSCH.001 JOBS.003 3 2016-11-02 16:00:00 2 (=One) Cale.002 TUESDAY 1
1000 JSCH.001 JOBS.003 3 2016-11-02 16:00:00 2 (=One) Cale.002 STATIC 1

 

Für manche JOBs finden sich mehrere Zeilen in der Tabelle, nämlich eine für jede Kalenderbedingung. Anhand der Ergebnisse der Kalenderbedingungen (die stehen in Cale_Active) müssen wir jetzt noch berechnen, ob die TASK am betroffenen Tag ausgeführt wurde.

Die 3 Möglichkeiten haben wir schon besprochen. Welche dafür für einen bestimmten JOB zutrifft, steht in EJPP_CCType. Für jeden Task mit Kalenderbedingungen gibt es einen EJPP_CCType (1, 2 oder 3) und binäre Ergebnisse für die Bedingungen (Cale_active).

Im Beispiel in der Tabelle hat das EJPP_Object JOBS.001 den EJPP_CCType 3 und für die zwei Bedingungen MONDAY und STATIC die Angaben 1 und 0. EJPP_CCType 3 bedeutet, dass der JOB nur ausgeführt wird, wenn keine der Bedingungen 1 ist. Das können wir in eine Formel übersetzen:

Max(Cale_Active) = 1

Entsprechend ergeben sich die folgenden Formeln für die 3 möglichen Werte von EJPP_CCType:

Condition EJPP_CCType Formel
all conditions match 1 Min(Cale_Active) = 1
no condition matches 3 Max(Cale_Active) = 0
one condition matches 2 Max(Cale_Active) = 1
All Items Description Your Total:

 

Jetzt können wir die gesamte Abfrage zusammensetzen:

-- Does NOT consider timezones
--Code is T-SQL, Oracle code in comments
with timeouttasks as (
    SELECT EH_Client, EH_Name, EJPP_Lnr, EJPP_Object, EJPP_ErlstSttimeT,
    CASE cast(EJPP_CCType as char(1))
        WHEN '1' THEN 'ALL'  WHEN '2' THEN 'ONE'  WHEN '3' THEN 'NO'
        ELSE EJPP_CCType
    END as CaleCondition
    , EJPPC_CaleName, EJPPC_CaleKeyName
    , substring(OKD_Content, 31*(month(EJPP_ErlstSttimeT)-1)+day(EJPP_ErlstSttimeT), 1) as CaleActive                    -- T-SQL
    --, substr(OKD_Content, 31*(to_char(EJPP_ErlstSttimeT, 'MM')-1)+to_char(EJPP_ErlstSttimeT, 'DD'), 1) as CaleActive   --Oracle
    from EH
    Inner join EJPP on EH_AH_IDNR = EJPP_AH_IDNR
    left join EJPPC on EH_AH_IDNR = EJPPC_AH_IDNR and EJPP_Lnr = EJPPC_EJPP_Lnr
    left join ( OH
        inner join OKB on OH_IDNR = OKB_OH_IDNR
        inner join OKD on OH_IDNR = OKD_OH_IDNR and OKB_Lnr = OKD_OKB_Lnr
        ) on OH_Client = EH_CLient and EJPPC_Calename = OH_Name and EJPPC_CaleKeyName = OKB_Name 
        and OKD_Year = year(EJPP_ErlstSttimeT)               -- T-SQL
        --and OKD_Year = to_char(EJPP_ErlstSttimeT, 'YYYY')  --Oracle
    where EH_Otype = 'JSCH'  And EJPP_Status = 1941  and ejpp_active = 1
    --and EH_STATUS = 1550 --include this filter to consider only active JSCH objects
), timeoutminmax as (
    select timeouttasks.* 
    , MIN(CaleActive) OVER(partition by EH_CLient, EH_Name, EJPP_Lnr) as MinVal
    , MAX(CaleActive) OVER(partition by EH_CLient, EH_Name, EJPP_Lnr) as MaxVal
    from timeouttasks
)
select distinct EH_Client, EH_Name, EJPP_Object, EJPP_Lnr, EJPP_ErlstSttimeT
from timeoutminmax
where 
    CaleCondition is NULL
      OR (    CaleCondition = 'ONE' and MaxVal = 1
    ) OR (    CaleCondition = 'ALL' and MinVal = 1
    ) OR (    CaleCondition = 'NO'  and MaxVal = 0
    )
ORDER BY EH_Client, EH_Name, EJPP_ErlstSttimeT;

Ein guter Grund, SQL zu nutzen

Dieses Script ist ziemlich harter Tobak und nur für fortgeschrittene Anwender geeignet. Aber es verdeutlicht sehr schön, warum es sich lohnt, SQL zu lernen und sich mit der Automation Engine Datenbank zu beschäftigen. Mit einigen Zeilen Code kann man schnell verschiedene Informationen aus unterschiedlichen Tabellen zusammentragen und auswerten.

Ich habe dieses Beispiel übrigens in Orlando bei der Automic World vorgestellt. In meiner Präsentation stellte ich noch zwei weitere Gründe für SQL und die Datenbank vor. Die Folien des Vortrags können Sie sich hier herunterladen.

Zum Abschluss noch eine Anmerkung: Das Skript von oben ist etwas vereinfacht und sollte nur angewendet werden, wenn Sie das Script mit all seinen Einschränkungen verstehen. Einige wichtige Faktoren werden von dem Query nicht in Betracht gezogen, z.B., dass EJPP_ErlstStTimeT in UTC ist.
Kurz: Use at your own risk!

Auf jeden Fall zeigt das Script die Funktionsweise und inspiriert Sie vielleicht zu eigenen SQL-Abfragen.