Frage zu Locking (Vergabe von Nummern im Nummernkreis)

Forum für neue Firebird-Anwender.

Moderator: thorben.braun

Antworten
jhoehne
Beiträge: 8
Registriert: Di 11. Dez 2018, 09:19

Do 14. Feb 2019, 09:50

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?
--
Joachim
vr2
Beiträge: 21
Registriert: Fr 13. Apr 2018, 00:13

Do 14. Feb 2019, 12:34

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
jhoehne
Beiträge: 8
Registriert: Di 11. Dez 2018, 09:19

Do 14. Feb 2019, 17:22

Super Idee, danke!
--
Joachim
bfuerchau
Beiträge: 49
Registriert: Mo 7. Mai 2018, 18:09

Do 14. Feb 2019, 19:02

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.
Antworten