Ich bin dabei, unsere Software von einer anderen Datenbank (Advantage Database Server) nach Firebird umzustellen. Für eine besondere Artikelnummer muss dabei ein Nummernkreis verwendet werden, d.h. es gibt nur eine begrenzte Zahl von Nummern, und es werden Nummern sowohl entnommen als auch wieder hinzugefügt. Aus dem Grund fällt ein Generator flach.
Damit mehrere User zeitgleich Nummern vergeben können, habe ich bislang die Vergabe mit einer besonderen "Lock"-Tabelle koordiniert: die Tabelle enthält genau einen Datensatz mit einem Wert. Die Vergaberoutine editiert und lockt damit den Datensatz, vergibt die Nummer und hebt danach den Lock wieder auf. Jeder weitere parallele Zugriff bekommt vom Datenbanksystem der Meldung, der Datensatz ist gelockt, wartet dann eine kurze Zeit und versuchts erneut. Das hat schon bei Paradox/BDE super funktioniert und beim ADS ebenfalls. Aber wie mache ich das mit Firebird?
Frage zu Locking (Vergabe von Nummern im Nummernkreis)
Moderator: thorben.braun
In einer Stored Procedure als erstes einen Generator abfragen, der nur benutzt wird, weil er transaktionsübergreifend ist. Im Grunde eine Semaphore.
Ist der Generator 0, dann als erstes den Generator hochsetzen, dann die Aktion durchführen, dann (auch bei eventueller exception) den Generator zurücksetzen
Ist er > 0, dann raus mit Hinweis
Grüße, Volker
Ist der Generator 0, dann als erstes den Generator hochsetzen, dann die Aktion durchführen, dann (auch bei eventueller exception) den Generator zurücksetzen
Ist er > 0, dann raus mit Hinweis
Grüße, Volker
Das mit dem Generator klappt nicht, da zwischen Abfrage und Änderung zu viel Zeit vergeht und somit mehrere parallele Abfragen zum selben Ergebnis, nämlich 0, kommen können.
Die Einzige Funktion, die tatsächlich auch mit LOCK-Timeout (Verbindungseigenschaft) arbeitet, ist ein "Select ... for update".
Der "for update" wartet so lange, bis die Sperre aufgehoben ist.
DIes passiert entweder mit Commit oder mit einem Rollback.
Also ist folgende Reihenfolge möglich:
Tabelle "GLOBALLOCK" mit einem Int-Feld=Key = 1 und einer Zeile erstellen (Global 1x)
- Transaktion starten
- Select Key from Globallock where Key = 1 for update
- Aktion durchführen
- commit
Da i.d.R. diese Transaktion kürzer ist als das Wait-Timeout, bekommst du Zugriffe Datenbankweit sequentialisiert.
Das schöne daran ist, die FB übernimmt auch noch das Synchronisieren, da du vom Client keine Schleife programmieren must.
Wenn die Aktion dann doch mal länger dauern sollte, bekommst du eine Fehlermeldung "Waittimeout" und gehst in die Wiederholung.
Auf diese Weise lassen sich auch verschiedene Aktionen (Key = 1, 2, 3, ...) sequentialisieren.
Auf dieselbe Weise kann man somit auch Beleg-Locks erstellen.
Tabelle mit einem Unique-Key erstellen.
- Transaktion starten
- Insert in die Tabelle
- Bei Duplicate Key => Beleg in Bearbeitung
- Wenn Bearbeitung fertig, dann Delete Key
Dies macht man in einer 2. Verbindung während man in seiner 1. Verbindung arbeitet.
Somit kann ich unterschiedliche "Bearbeitungssperren" setzen, und diese einem User ohne Wartezeiten signalisieren.
Das schöne daran ist, egal wieviele Sperren ich damit setze, bei Verbindungsabbruch wird ein Rollback gemacht und die Sperren sind automatisch aufgehoben.
Die Einzige Funktion, die tatsächlich auch mit LOCK-Timeout (Verbindungseigenschaft) arbeitet, ist ein "Select ... for update".
Der "for update" wartet so lange, bis die Sperre aufgehoben ist.
DIes passiert entweder mit Commit oder mit einem Rollback.
Also ist folgende Reihenfolge möglich:
Tabelle "GLOBALLOCK" mit einem Int-Feld=Key = 1 und einer Zeile erstellen (Global 1x)
- Transaktion starten
- Select Key from Globallock where Key = 1 for update
- Aktion durchführen
- commit
Da i.d.R. diese Transaktion kürzer ist als das Wait-Timeout, bekommst du Zugriffe Datenbankweit sequentialisiert.
Das schöne daran ist, die FB übernimmt auch noch das Synchronisieren, da du vom Client keine Schleife programmieren must.
Wenn die Aktion dann doch mal länger dauern sollte, bekommst du eine Fehlermeldung "Waittimeout" und gehst in die Wiederholung.
Auf diese Weise lassen sich auch verschiedene Aktionen (Key = 1, 2, 3, ...) sequentialisieren.
Auf dieselbe Weise kann man somit auch Beleg-Locks erstellen.
Tabelle mit einem Unique-Key erstellen.
- Transaktion starten
- Insert in die Tabelle
- Bei Duplicate Key => Beleg in Bearbeitung
- Wenn Bearbeitung fertig, dann Delete Key
Dies macht man in einer 2. Verbindung während man in seiner 1. Verbindung arbeitet.
Somit kann ich unterschiedliche "Bearbeitungssperren" setzen, und diese einem User ohne Wartezeiten signalisieren.
Das schöne daran ist, egal wieviele Sperren ich damit setze, bei Verbindungsabbruch wird ein Rollback gemacht und die Sperren sind automatisch aufgehoben.