Lade...
 

Transaktionen

Transaktionen

InstantView® verwaltet Datenbank-Transaktionen automatisch.
Der InstantView®-Interpreter versucht, eine Transaktion so spät wie möglich zu beginnen: Die erste Anweisung, die nur innerhalb einer Transaktion operieren kann, startet die Datenbank-Transaktion.
Am Ende der Anweisungsfolge wird sie automatisch beendet (commit), und damit werden veränderte bzw. neu erzeugte Objekte in die Datenbank geschrieben.
Tritt ein Fehler auf, so wird die Transaktion abgebrochen (abort). Veränderte Objekt verbleiben im (alten) Zustand vor der Änderung, neu erzeugte Objekte existieren nicht mehr. 
InstantView® reflektiert diese Rücknahme von Veränderungen in der Datenbank indem parallel zu den Datenbank-Transaktionen logische Transaktionen verwaltet werden.
So wird zum Beispiel verhindert, dass eine InstantView® -Variable auf ein persistentes Objekt verweist, das gar nicht mehr existiert.
Die Konsistenz zwischen Datenbank und InstantView® wird erreicht, indem bei Transaktions-Abbruch

  • alle Modulvariablen und globale Variablen auf den Wert zu Transaktionsbeginn zurückgesetzt werden, 
    Lokale Variablen werden nicht zurückgesetzt!
  • transiente Collections wieder genau die Objekte als Elemente enthalten, auf die sie zu Beginn der Transaktion verwiesen haben,
  • die innerhalb der Transaktion durchgeführten Kommandos SetLayer, SetDomain und SetPattern auf den Wert zu Transaktionsbeginn zurückgesetzt werden,
  • die folgenden mittels SystemObject zu setzenden System-Parameter - wenn sie innerhalb einer Transaktion verändert wurden - bei Transaktionsabbruch zurückgesetzt werden:

     

    Parameter SystemObject Methode
    Gültigkeits-Datum SetValidityDate
    Check-Mask für Gültigkeit (der Objekte) SetCheckMask
    Sitzungs-Datum (session date) SetSessionDate
    Timestamp bei Update-Operationen ein- bzw. ausschalten SetTimeStamp
    Obergrenze für ein Datum - CX_DATE SetDateLimit
    Voreinstellung Maßeinheit für CX_VALUE SetDefaultUnit
    Voreinstellung Währung SetDefaultCurrency
    aktuelles Locale SetLocaleByName, SetLocaleByObject
    Message für Variablen-Abfrage bei CX_FORMULA (bind message) SetBindMessage
    Umrechungstabellen für Einheiten (bzw. Währungen) SetRate, RegisterRate, DeregisterRate, AddOverwriteRate
  • Veränderungen des PlugSpace zurückgenommen werden  - PlugSpace, PlugSpacePush, PlugSpacePop
  • der eingestellte Sprach-Index zurückgesetzt wird  - Language
  • innerhalb der gerade abgebrochenen Transaktion geöffnete Windows wieder geschlossen werden,
  • die Windowobjekte ObjectList, ObjectComboBox, ListView und ObjectTree auf den Zustand zu Transaktionsbeginn zurückgesetzt werden (sofern sie innerhalb der Transaktion neue oder andere persistente Objekt dargestellt haben),
  • innerhalb der Transaktion initialisierte Module als nicht initialisiert betrachtet werden. Bei der nächsten Message, die sie empfangen, wird INITIALIZE erneut implizit aufgerufen.

Achtung: Veränderungen transienter Objekte werden nicht zurückgenommen!
Beispiele für das Verhalten der obengenannten Elemente des ClassiX®-Systems finden Sie hier.   

Zur Reduzierung von Locking-Konflikten kann es sinnvoll sein, Transaktionen explizit zu steuern (der InstantView®-Interpreter kennt den am spätesten möglichen Zeitpunkt zum Start einer Transaktion, aber nicht den frühsten Zeitpunkt zum Beenden!).

Hinweis:
- Wenn eine Transaktion explizit mit EndTXN / BeginTXN gesteuert wird, betrifft das auch das logische Transaktionshandling von InstantView®.
  Für einen Transaktionswechsel mit dem Zweck, Address Space freizugeben, soll die Methode FreeAddressSpace des Object-Managers benutzt werden!

- Transaktionswechsel ist nicht überall erlaubt - siehe Anweisung iterate - und wird dann ignoriert. Im Log-File erscheint eine Warnung.

Außerdem kann man  Datenbanken im MVCC-Modus (Multi Version Concurrency Control - siehe ObjectStore Dokumentation) öffnen: Locking-Konflikte werden weitgehend vermieden, da die im MVCC-Modus gelesenen Objekte von anderen Clients geändert werden können. Im MVCC-Modus sieht ein Client nicht immer die aktuellste Version eines Objekts, alle Objekte aber in einem Zustand, der in sich konsistent ist.

Lock-Timeouts

Lock-Timeouts können jederzeit während einer Schreibtransaktion auftreten, wenn ein Client zu lange auf ein Datenbank-Lock warten muss. Damit jeder Client eine konsistente Sicht auf die Datenbank erhält, stellt die Datenbank sicher, dass zu jedem Zeitpunkt entweder beliebig viele Clients eine Page gleichzeitig lesen können (n Read-Locks), oder ein einziger Client die Page beschreiben kann (1 Write-Lock).

Beim Versuch eine Page zu schreiben/lesen kann es also dazu kommen, dass ein Client noch nicht lesen/schreiben darf, weil ein anderer Client noch ein Lock hält, welches dies verhindert. Wartet der Client zu lange auf das Lock, kommt es zu einem Lock-Timeout-Fehler.

Lock-Timeouts reduzieren/vermeiden:

  1. Schreibtransaktionen so kurz wie möglich fassen. Während einer Schreibtransaktion sollten keine langen Berechnungen durchgeführt werden oder gar Nutzerinteraktion zwischendurch erforderlich sein. Berechnungen sollten soweit möglich vorher in einer Lesetransaktion im transienten Raum durchgeführt werden und anschließend die Änderungen in einer kurzen Schreibtransaktion in den persistenten Raum übertragen werden. 
  2. Lese-Transaktionen im MVCC-Modus durchführen (BeginTXN(READ)). Ein Client im MVCC-Modus kann keine anderen Clients blockieren. Clients die häufig lesen und schreiben müssen, müssen hierdurch jedoch beim Transaktionswechsel die Datenbank neu öffnen, was je nach Größe der Datenbank mehr Zeit kostet und der gesamte Cache geht dabei verloren.
  3. Ein weiterer Ansatz wäre die Anwendung umzustrukturieren, dass es nur noch einen Client gibt, der alle Schreiboperationen durchführt (eine Art Datenbankworker) und die restlichen Clients lesen die Datenbank im MVCC-Modus. Dieser Ansatz funktioniert jedoch nur für sehr spezielle Anwendungen und falls zwei Clients das gleiche Objekt beschreiben wollen, ist ein Lost-Update sehr wahrscheinlich, was zu inkonsistenten Daten führen kann.
  4. Transaktion mittels RetryTXN automatisch wiederholen. Dies ist eine Möglichkeit, um Lock-Timeouts vor dem Nutzer zu verbergen, jedoch kann dieser Ansatz das Problem potenziell verschlimmern, da das zugrundeliegende Problem damit nicht gelöst ist und der Nutzer nun potenziell länger warten muss, bis die Fehlermeldung erscheint. Hinzu kommt, dass der Client, der die Transaktion wiederholt selbst Datenbank-Locks hält, die wiederum andere Clients stärker blockieren können, da die Locks durch die Wiederholung über einen längeren Zeitraum bestehen bleiben.

Deadlocks

Ein Deadlock tritt auf, wenn zwei Clients die gleichen 2 Locks in unterschiedlicher Reihenfolge anfragen und beide ihr erstes Lock bereits erhalten haben. Keiner der beiden Clients kann in dem Fall seine Transaktion fortsetzen außer der andere Client gibt sein bereits erhaltenes Lock wieder auf. ObjectStore erkennt eine solche Situation und entscheidet zufällig, welcher der beiden Clients die Transaktion fortsetzen darf, während die Transaktion des anderen Clients automatisch abgebrochen wird.

Deadlocks reduzieren/vermeiden:

  1. Deadlocksituationen untersuchen und Anwendungsablauf so anpassen, dass Objekte möglichst immer in der gleichen Reihenfolge angefasst werden (im Sinne von Lock-Ordering). Dies ist bei weitem die beste Lösung für Deadlocks, da hiedurch Deadlocks komplett verhindert werden.
  2. Lese-Transaktionen im MVCC-Modus durchführen (BeginTXN(READ)). Ein Client im MVCC-Modus kann keine anderen Clients blockieren. Clients die häufig lesen und schreiben müssen, müssen hierdurch jedoch beim Transaktionswechsel die Datenbank neu öffnen, was je nach Größe der Datenbank mehr Zeit kostet und der gesamte Cache geht dabei verloren.
  3. Deadlocksituationen untersuchen und mittels BeginLock(WRITE) in beiden Anwendungsfällen ein Synchronisationsobjekt locken, welches sozusagen den Einstiegspunkt in den Anwendungsfall darstellt und sicherstellt, dass zu jedem Zeitpunkt nur ein Client Zugriff auf die betroffenen Objekte hat. Dieser Ansatz hat jedoch den Nachteil, dass hierdurch nicht betroffene Clients (gleicher Anwendungsfall aber andere Objekte) potenziell unnötig ausgebremst werden und Transaktionen länger offen bleiben als nötig, was wiederum zu Lock-Timeouts führen kann.
  4. Die fehlschlagende Transaktion mittels RetryTXN automatisch wiederholen. Der Client dessen Transaktion durch den Deadlock abgebrochen wurde, kann seine Transaktion hiermit automatisch wiederholen, jedoch löst dieser Ansatz das zugrundeliegende Problem nicht und kann bei vielen aktiven Clients dazu führen, dass der Deadlock in der wiederholten Transaktion mit einem anderen Client erneut auftritt. Dies kann im Extremfall dazu führen, dass die Deadlocks weiterhin genauso häufig gemeldet werden und sich die Wartezeiten lediglich erhöhnen. Durch die wiederholte Transaktion werden Locks über einen längeren Zeitraum gehalten, was wiederum zu vermehrten Lock-Timeouts führen kann.
  5. Schreibtransaktionen so kurz wie möglich zu fassen kann Deadlocks auch reduzieren, jedoch wird hierdurch das Problem potenziell nur aufgeschoben, da die Deadlocks wieder auftreten können, sobald die Anzahl der Clients erhöht wird.
  6. Mittels BeginLock(WRITE, DATABASE) oder CX_TRANSACTION_MANAGER։։SetDefaultLockMode kann im Extremfall für jede Transaktion die gesamte Datenbank gelocked werden. Die Datenbank arbeitet damit quasi im Single-User-Betrieb und Transaktionen werden nicht mehr parallel durchgeführt. Ein Client muss mit seiner Transaktion warten, bis alle vorheirgen Clients ihre Transaktion abgeschlossen haben. Dieser Ansatz macht Deadlocks unmöglich, jedoch sorgt er auch für sehr lange Wartezeiten und kann die Zahl der Lock-Timeouts (vor allem bei vielen Clients) in die Höhe treiben. Dieser Ansatz sollte nur für extrem kurze Schreibtransaktionen gewählt werden und stellt eher eine Notlösung dar. Die Performance der Anwendung leidet extrem unter diesem Ansatz.

 

Statements:

Hinweis: Das ClassiX®-System führt Selbst-Tests durch, um die oben beschriebenen Automatismen zu überwachen. Die Fehler, die dabei entdeckt werden können, sind in dieser Tabelle zusammengefasst.