Definicja procesu (5) – obsÅ‚uga błędów

Dziś poruszymy temat obsługi błędów w procesach. Zacznijmy od tego, że w NGinn każde zadanie, jeśli zostanie uruchomione, może zakończyć się z następującymi rezultatami:

  • zakoÅ„czenie normalne (prawidÅ‚owe, na wyjÅ›ciu otrzymujemy dane wyjÅ›ciowe z zadania)
  • anulowanie
  • zakoÅ„czenie na skutek błędu

ZakoÅ„czenie normalne to przypadek omawiany już wczeÅ›niej – po zakoÅ„czeniu zadania sÄ… produkowane tokeny w miejscach do których prowadzÄ… połączenia wychodzÄ…ce (oczywiÅ›cie jeÅ›li mamy split XOR lub OR to sÄ… jeszcze sprawdzane warunki dla tych przejść). Po anulowaniu tokeny wyjÅ›ciowe nie sÄ… produkowane (jest taka opcja, ale o tym kiedy indziej). Po zakoÅ„czeniu z błędem natomiast mamy pewne możliwoÅ›ci zareagowania na ten błąd i odpowiedniego dostosowania dalszego biegu procesu – w tym celu używamy ‘error handlerów’ czyli specjalnych przejść uaktywnianych tylko w  razie błędu:

Obsługa błędu (1)

Obsługa błędu (1)

 

W tym procesie normalny przebieg jest taki: start->T1->T2->end. Natomiast zadanie T1 ma dodatkowe przejÅ›cie wychodzÄ…ce, oznaczone na rysunku dwoma czerwonymi ukoÅ›nikami. To przejÅ›cie jest uruchamiane w razie zakoÅ„czenia zadania T1 z błędem (tzn jeÅ›li T1 siÄ™ ‘wywali’ to token pojawi siÄ™ na przejÅ›ciu T1->T1 error a nie na T1->T2), czyli kolejnym uruchomionym zadaniem bÄ™dzie ‘T1 error’ – nasz ‘error handler’ dla T1.

No dobra, Å›wietnie: potrafimy już przyczepić obsÅ‚ugÄ™ błędów do jakiegoÅ› zadania. Ale przecież w procesie wiele rzeczy może siÄ™ nie udać, jeÅ›li zaczniemy wszÄ™dzie doczepiać obsÅ‚ugÄ™ błędów to szybko nasz diagram zamieni siÄ™ w chaotycznÄ… plÄ…taninÄ™ strzaÅ‚ek. A poza tym: co siÄ™ stanie jeÅ›li zadanie siÄ™ ‘wywali’ a nie ma zdefiniowanej obsÅ‚ugi błędów? Å»eby odpowiedzieć na powyższe wÄ…tpliwoÅ›ci zobaczmy kolejny diagram:

 

Obsługa błędów (2)

Obsługa błędów (2)

 

Na tym schemacie mamy zadanie blokowe (to z przerywanÄ… ramkÄ…) do którego dołączona jest obsÅ‚uga błędu (T1 error). Zapis taki oznacza że w przypadku wystÄ…pienia jakiegokolwiek błędu w zadaniu blokowym obsÅ‚ugujemy ten błąd w ‘T1 error’. A dokÅ‚adnie: jeÅ›li w trakcie wykonania zadania blokowego pojawi siÄ™ błąd w zadaniu ‘Task1′ lub ‘Task2′ to caÅ‚e zadanie blokowe zostanie zakoÅ„czone z błędem. JeÅ›li byÅ‚y w nim jakieÅ› inne aktywne zadania w momencie wystÄ…pienia błędu to zostanÄ… one anulowane – wszystko co byÅ‚o wewnÄ…trz zadania blokowego zostaje zakoÅ„czone i dopiero wtedy caÅ‚e zadanie blokowe koÅ„czy siÄ™ z błędem. Informacje o błędzie sÄ… propagowane w górÄ™ od zadania które spowodowaÅ‚o błąd – czyli kod błędu w zadaniu blokowym bÄ™dzie taki sam jak kod błędu zwrócony przez zadanie wewnÄ™trzne które zakoÅ„czyÅ‚o siÄ™ błędem. MÄ™tne? Czy jesteÅ› czytelniku programistÄ…? To pomyÅ›l o konstrukcji try-catch: wszystko co jest wewnÄ…trz zadania blokowego to ‘try’, zaÅ› error handler z czerwonymi ukoÅ›nikami to ‘catch’.

A co jeÅ›li zadanie blokowe także nie ma swojej obsÅ‚ugi błędów? Wtedy NGinn propaguje błąd dalej w górÄ™ aż dojdzie na poziom zadania blokowego które już ma error handler – czyli ta konstrukcja może być dowolnie głęboko zagnieżdżona. Tylko że do tej pory zakÅ‚adam że w koÅ„cu wyjdziemy na taki poziom gdzie ta obsÅ‚uga błędu jest. A co gdy nie ma? W koÅ„cu idÄ…c w górÄ™ dojdziemy na poziom caÅ‚ego procesu – do procesu już nie można sobie doczepić ‘error handlera’ bo nie ma do czego. Czyli wtedy mamy sytuacjÄ™ że nasz ‘wyjÄ…tek’ nie jest obsÅ‚ugiwany w ramach procesu  (dla programistów: unhandled exception). W normalnych programach taka sytuacja jest awariÄ… – system wyÅ›wietla informacjÄ™ o błędzie i natychmiast koÅ„czy dziaÅ‚anie programu. A u nas? NGinn mógÅ‚by wtedy wpisać do logu że ‘proces XYZ niespodziewanie zakoÅ„czyÅ‚ siÄ™ z powodu nieobsÅ‚użonego błędu’ i usunąć ten proces, ale to nie jest dziaÅ‚anie cywilizowane w Å›wiecie procesów biznesowych – pamiÄ™tajmy że ten proces reprezentuje jakieÅ› dokumenty, ludzkie dziaÅ‚ania, decyzje w firmie i nie może tak sobie zniknąć.

Dlatego w NGinn takÄ… sytuacje traktujemy jako typowÄ…. To znaczy: normalne jest że definicja procesu nie zawiera obsÅ‚ugi błędów i takie procesy też powinny dziaÅ‚ać i dawać spodziewane wyniki. A jeÅ›li wystÄ…pi błąd to ma zostać o tym powiadomiony administrator systemu i powinien on mieć szansÄ™ rÄ™cznej interwencji w celu usuniÄ™cia lub obejÅ›cia problemu. Jak to siÄ™ dzieje? Otóż gdy NGinn w trakcie obsÅ‚ugi błędu stwierdzi że w caÅ‚ym procesie brak jakiejkolwiek obsÅ‚ugi tego błędu to błąd ten nie bÄ™dzie propagowany w górÄ™. Zamiast tego winowajca (zadanie które ten błąd wygenerowaÅ‚o) jest oznaczane jako ‘FailedActive’ – tzn zakoÅ„czone z błędem, ale jeszcze nie wiadomo co z nim zrobić. Zadanie takie już siÄ™ zakoÅ„czyÅ‚o, ale dla zadania nadrzÄ™dnego jest nadal aktywne i nie generuje żadnych dalszych zdarzeÅ„. Administrator systemu może sobie takie przypadki odszukać (albo nawet zostać automatycznie powiadomiony) i wtedy może przeprowadzić nastÄ™pujÄ…ce dziaÅ‚ania:

  • ponownie uruchomić zadanie (np gdy błąd to tylko przejÅ›ciowy problem) 
  • potwierdzić że to zadanie to rzeczywiÅ›cie błąd i przekazać jego obsÅ‚ugÄ™ NGinn (ale wtedy proces rzeczywiscie zostanie zakoÅ„czony z błędem)
  • ‘oszukać’ NGinn i powiedzieć że to zadanie jednak zakoÅ„czyÅ‚o siÄ™ sukcesem (wtedy musi jednak dostarczyć prawidÅ‚owe dane wyjÅ›ciowe dla tego zadania, tak jakby rzeczywiÅ›cie zakoÅ„czyÅ‚o siÄ™ ono poprawnie)

Czasami będzie jeszcze musiał poprawić definicję procesu jeśli błąd powstał na skutek nieprawidłowości w definicji. 

Sposób udostępniana w/w operacji dla administratora może być różny. NGinn oferuje API do tego celu a udostępnienie tego w postaci GUI lub command line to zadanie dla aplikacji używającej NGinn.

Acha, oto definicja pierwszego procesu z dzisiejszego artykuÅ‚u. W sekcji ‘flows’ jest zdefiniowana obsÅ‚uga błędu dla zadania T1 – chodzi o przejÅ›cie z taskOutPort=”Error”:


<?xml version="1.0" encoding="utf-8"?>
<process version="1" name="Failure" xmlns="http://www.nginn.org/WorkflowDefinition.1_0.xsd" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <dataTypes>
    </dataTypes>
    <variables>
		<variable name="v1" type="int" dir="In" required="true" />
    </variables>
    <body>
        <places>
            <place id="start" type="Start" />
            <place id="end" type="End" />
        </places>
        <tasks>

			<task id="T1" type="ReceiveMessage">
            </task>

			<task id="T2" type="Timer" splitType="AND">
            </task>

            <task id="T3_F" label="T1 error" type="Empty" splitType="AND">
			</task>

        </tasks>
        <flows>
            <flow from="start" to="T1" />
            <flow from="T1" to="T2" />
			<flow from="T1" taskOutPort="Error" to="T3_F" />
			<flow from="T3_F" to="end" />
			<flow from="T2" to="end" />
        </flows>
    </body>
</process>

Let us talk about
Name and Mail are required
Join the discuss