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:
- Tasks identifizieren, die für den betroffenen Zeitraum geplant waren und mit dem Status ENDED_TIMEOUT endeten
- Kontrollieren, ob es Kalender-Bedingungen gibt, die eine Ausführung der Task sowieso verhindert hätten
- 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.