|
Serielle Bearbeitung von APEX-Sessions erzwingen
von
Peter Raganitsch, click-click IT Solutions e.U., Wien, mailto:Peter[dot]Raganitsch[at]click[minus]click[dot]at
Oracle APEX-Anwendungen sind echte Web-Anwendungen: sie haben keine dedizierte Datenbankverbindung
(wie z.B. Oracle Forms) und verwenden das normale HTTP-Protokoll.
Der Client schickt also eine Anfrage an den Webserver und wartet dann auf auf eine
(HTML)-Antwort, die vom Browser dargestellt wird.
Dabei ist es dem Browser egal, ob er die vorherige Antwort schon bekommen hat. Wenn
der User einen Link oder Button klickt, wird die entsprechende Anfrage an den Webserver
gestellt.
Nun kann es sein, dass der Webserver noch mit der vorherigen Anfrage beschäftigt ist,
wenn die neue Anfrage vom User kommt. Dann wird die neue Anfrage einfach in einem weiteren
Thread parallel zur vorherigen Anfrage bearbeitet.
Was im Falle von einfachen HTML-Dateien die vom Server angefordert werden kein Problem ist,
wird bei APEX Anwendungen mitunter problematisch.
Der Webserver erstellt hier bei der Bearbeitung einer Anfrage eine Datenbankverbindung
und führt dort die APEX-Funktion "f" aus. Diese Funktion kennen sie sicher alle aus der
Adresszeile von APEX Anwendungen.
Solange von der Datenbank gelesen wird, ist das auch alles kein Problem, im schlimmsten
Fall werden hier Daten gelesen, die keiner mehr empfangen will.
Bei schreibenden Operationen kann es hier aber zu Lock-Konflikten, falschen oder
überschriebenen Daten kommen.
Folgendes Beispiel-Szenario:
- Beim Aufruf einer Seite wird im Before-Header-Process eine Collection aufgebaut, die von einem Report auf der gleichen Seite verwendet und auch von den Folgeseiten gelesen wird.
- Nun kann es vorkommen, dass der Aufbau der Collection mehrere Sekunden dauert
- Ist der Benutzer ungeduldig und klickt mehrmals auf den Menü-Link, der unsere Collection-Seite aufruft, dann werden neue Anfragen an den Server gestellt, bevor die alte abgearbeitet ist
- Das führt dazu, dass unser Before-Header-Process parallel mehrmals (1x pro User-Klick) in der Datenbank ausgeführt werden
- nun legt die erste Session die Collection gerade an, während die zweite Session startet
- die zweite Session sieht die Collection allerdings noch nicht und versucht diese anzulegen
- währenddessen wird die erste Session fertig, committed die Anlage der Collection und versucht die Antwort an den User zu retournieren, der allerdings inzwischen nicht mehr darauf, sondern auf die Antwort von Session 2 wartet
- Session 2 ist mit dem Aufbau der Collection fertig, löst ein commit aus und bekommt einen Fehler: Collection already exists
Dieses Verhalten ist für den Benutzer ziemlich frustrierend, da er zuerst lange warten muss und dann noch eine Fehlermeldung angezeigt bekommt.
Abhilfe kann hier ein erzwungenes Serialisieren der Ausführungen der Prozesse schaffen.
Dazu wird vor dem Erstellen der Collection die Prozedur ApexLib_Serialize.serializeSessions aufgerufen, die mithilfe der SESSION-ID und dem Package DBMS_LOCK die Ausführung der Prozesse in der Datenbank serialisiert, d.h. sie werden nicht neben-, sondern hintereinander ausgeführt.
Das lässt den Benutzer u.U. länger warten, allerdings bekommt er sicher eine saubere Collection und damit die Daten die er sehen will.
Der Code dazu:
Im Page-Process sieht es dann in etwa so aus:
Das Ergebnis ist wie erwartet, egal wie oft der Benutzer die Seite anfordert, es kommt
zu keinen Überschneidungen in der Bearbeitung und zu keiner Fehlermeldung.
Alle anderen Benutzer können übrigens ungestört weiterarbeiten, da dieser Mechanismus
sich auf die SESSION-ID stützt.
Was tun, wenn kein Zugriff auf DMBS_LOCK besteht?
Anstelle des DBMS_LOCK Packages kann hier natürlich auch mit einer sogenannten
MUTEX-Tabelle
gearbeitet werden.
Dazu wird eine Tabelle erstellt, in die die fragliche APEX-Session-ID eingetragen und
beim Serialisieren mit SELECT FOR UPDATE WAIT xxxxx; angefordert wird.
Selbstverständlich gibt es auch andere Methoden, um mehrfache Klicks auf denselben
Link/Button/Menüeintrag auszuschliessen (verstecken, ausgrauen, disablen,...), allerdings kann
am Client jedweder Check ausgehebelt oder übergangen werden, deswegen ist auch immer die
serverseitige Überprüfung nötig.
Zurück zur Community-Seite
|