Timeout-Zeit bei Lock

Hallo zusammen,

ich habe eine Frage zum Verhalten beim Zugriff mehrerer Programme auf eine Datenbank.

Folgendes Beispiel: Programm1 schreibt in eine Datenbank, und zwar ziemlich viel, was dann auch ziemlich lange dauert. Programm2 will auch schreiben, kann aber nicht, weil die DB durch Programm1 gesperrt ist. Programm2 muss also warten. Die Frage ist nun: Wie stelle ich ein, wie lange Programm2 darauf wartet, endlich schreiben zu können? Wenn das Warten nämlich zu lange dauert, dann will ich Programm2 beenden und es dann später noch einmal probieren.

Bei Informix gibt es den Befehl “SET LOCK MODE TO [arg]”. Wenn man als Argument “WAIT seconds” angibt, dann wartet Programm2 seconds Sekunden und kommt dann mit einer Fehlermeldung zurück. Wie realisiere ich das bei Postgres?

MfG Udo

Eigentlich sperrt die DB so schnell nicht, kannst das mal näher beleuchten?


Andreas

Hm, also meiner Meinung nach ist ja für die Beantwortung der Frage nicht relevant, warum die Datenbank gesperrt ist. Sie ist halt von Programm1 gesperrt und nun kommt Programm2 und will auch schreiben - kann aber nicht und muss warten. Dafür muss es ja einen wie auch immer gearteten Timeout-Wert geben. Meine Frage ist nun, wie man den konfiguriert und wie man in Programm2 abfängt, dass die DB gesperrt und ein Schreibvorgang im Augenblick nicht möglich ist. Ich suche also (deswegen hab ich es mit angegeben) ein Analogon für dieses SET LOCK MODE vom Informix. MfG Udo

Zuerstmal: es wird nicht die “Datenbank” gesperrt sondern maximal ein Datensatz. Wobei die Möglichkeit besteht manuell (!) eine gesamte Tabelle zu sperren. Das bezieht sich aber immer auf einen schreibenden Zugriff. Der lesende Zugriff (SELECT …) wird dadurch niemals verhindert.

Solange die beiden Programme nicht den gleichen Datensatz mittels UPDATE ändern wollen, so werden sie sich auch nicht gegenseitig “sperren”.

Du musst schon etwas präziser werden wie genau die Zugriffe Deiner Programme aussehen.

Ein Möglichkeit einen Datensatz explizit zu sperren ist mit “SELECT … FOR UPDATE NOWAIT”. Wenn Du unbedingt pessimistic locking benötigst, so wird bleibt Dir nur jeden Datensatz der geändert werden soll, vorher mit “SELECT … FOR UPDATE NOWAIT” zu sperren. Wenn das nicht geht bekommst Du sofort einen Fehler. Einen “Timeout” gibt es dabei aber nicht, das müsstest Du selber in Deinem Programm implementiert (z.B. 10mal versuchen und dann aufgeben oder so ähnlich).

Aber nochmal: das ist eigentlich die schlechteste Methode mit einer relationalen DB zu arbeiten. Wenn die Programme nicht wirklich ständig die gleichen, identischen Datensätze ändern wollen, dann überlass das lieber der Datenbank.

Hallo castorp,

Das ist ja genau das Problem: beide Programme greifen SCHREIBEND auf die Datenbank zu und blockieren dadurch (hatte ich das schon erwähnt?).

Du musst schon etwas präziser werden wie genau die Zugriffe Deiner Programme aussehen.

Ein Möglichkeit einen Datensatz explizit zu sperren ist mit “SELECT … FOR UPDATE NOWAIT”. Wenn Du unbedingt pessimistic locking benötigst, so wird bleibt Dir nur jeden Datensatz der geändert werden soll, vorher mit “SELECT … FOR UPDATE NOWAIT” zu sperren. Wenn das nicht geht bekommst Du sofort einen Fehler. Einen “Timeout” gibt es dabei aber nicht, das müsstest Du selber in Deinem Programm implementiert (z.B. 10mal versuchen und dann aufgeben oder so ähnlich).

Aber nochmal: das ist eigentlich die schlechteste Methode mit einer relationalen DB zu arbeiten. Wenn die Programme nicht wirklich ständig die gleichen, identischen Datensäze ändern wollen, dann überlass das lieber der Datenbank.

Und wenn sie nun - wie in meinem Fall - ständig die gleichen, identischen Datensätze ändern wollen, was mache ich dann? Womit wir wieder bei der eigentlichen Frage wären …

Das mit dem NOWAIT ist mir bekannt, aber keine Lösung, weil ich dann für die Informix-Variante anderen Code brauche als für Postgres. Meine Hoffnung war, etwas für den Postgres-Zugriff zu finden, das genauso funktioniert wie bei Informix, aber das scheint nun leider nicht zu klappen, schade!

MfG Udo

Das wirst Du sowieso benötigen.

Wenn ein Programm unabhängig vom drunterliegenden DBMS ist, bedeutet das nur, dass das Programm auf allen Platformen gleichmässig langsam (und vermutlich schlecht) läuft.

Versuch einen vernünftigen Abstraktionslayer zu schreiben über den das Programm auf die DB zugreift und in dem Layer verwendest Du die ganzen DB spezifischen Features.

Wieso dauern deine Transaktionen überhaupt so lange?

All zu viel möchte/kann/darf ich dazu nicht sagen, nur so viel: ein Server startet bei eingehenden Modem-Verbinungen COM-Services, die die Daten abholen und in die DB schreiben. Da Modem nun mal langsam ist und viele Daten gesendet werden, dauert die Übertragung schon mal ein paar Minuten. So eine Modem-Verbindung (z. B. aus dem Ausland) ist auch heute noch recht teuer, also soll die zweite Verbindung nicht ewig warten, sondern nach ein paar Sekunden abbrechen und dem “Anrufer” sagen, er soll es später noch einmal probieren. Bei der Informix-Variante wurde das mit dem SET LOCK MODE realisert, und meine Hoffnung war ganz einfach, für Postgres ein Pendent zu finden. Am Quellcode der Services kann ich selber nämlich gar nicht so viel machen.

Es gibt da so eine neumodische Erfindung, nennt sich Internet :wink:

Abgesehen davon: das Konzept, auf beliebig lange Wartezeiten zu warten, hört sich für mich umständlich an.

Naja, das Programm soll ja eben genau nicht “beliebig”, sondern “definiert” lange warten. Was genau würde denn passieren, wenn Programm1 eine Tabelle mit UPDATE beschreibt und Programm2 auf die selbe Tabelle ebenfalls ein UPDATE machen will? Dann wartet Programm2, bis sein SQL-Statement ausgeführt wurde, selbst wenn es sich da um Minuten oder gar Stunden handeln kann, oder? Ich hätte halt gern (analog zum Ifx) die Möglichkeit, dem SQL-Statement (bzw. wie bei Ifx der ganzen Connection) mitgeben zu können, wie lange es maximal warten soll, um dann mit einem Fehlercode “Timeout” zurück zu kommen. Ich selber kann nur die SQL-Strings und nicht die Services anfassen.

Der Reiz, hier mal laut zu lachen, ist recht hoch :wink:

Ernsthaft: sollte man nicht da mal ansetzen? Es gibt Provider, die bieten managed PostgreSQL-Server und haben fette Anbindung im mehrstelligen GBit-Bereich. Mal so als Hinweis…

also soll die zweite Verbindung nicht ewig warten, sondern nach ein paar Sekunden abbrechen und dem “Anrufer” sagen, er soll es später noch einmal probieren. Bei der Informix-Variante wurde das mit dem SET LOCK MODE realisert, und meine Hoffnung war ganz einfach, für Postgres ein Pendent zu finden. Am Quellcode der Services kann ich selber nämlich gar nicht so viel machen.

Um mal ernst zu bleiben: die Daten erst mal ablegen und dann in einem rutsch in die DB spielen wäre keine Option?

Ansonsten: es gibt noch statement_timeout.

Session1

test=*# select * from foo for update;
 i |       t
---+----------------
 1 | eins
 2 | eins,zwei,drei
 3 | drei
 4 | drei,vier
(4 rows)

Time: 0,305 ms

Session 2:

test=# set statement_timeout to 1000;
SET
test=*# \timing
Timing is on.
test=*# update foo set t=t || 'new';
ERROR:  canceling statement due to statement timeout
test=!# rollback;
ROLLBACK

Andreas

Bin mir nicht sicher, ob es ganz genau das ist was Du brauchst, aber versuch es doch mal mit dem “allgemeine” statement_timeout:

@akretschmer: Zum Lachen ist es eigentlich nicht, sondern eher zum Heulen, aber was soll ich machen, an den Services kann ich nicht drehen, die sind in Stein gemeißelt.

Der Tipp mit dem statement_timeout scheint aber die Lösung zu sein, das werde ich ausprobieren. Vielen Dank schon mal für den Hinweis. Das Ding ist mir beim Durchsuchen der 2500 Seiten Postgres-Doku gar nicht aufgefallen :smiley: .

Da wollt ich nun auch auf “statement_timeout” hinweisen, aber die beiden anderen waren schneller.

Aber sag mal: in Stein gemeißelt ist bis auf die 10 Gebote von Moses gar nichts.
Und selbst da hat man die originalen Quellen noch nicht gefunden.

Überleg dir, wie lange du so ein überaltetes System supporten willst, oder ob es nicht an der Zeit ist, da mal über Auswege aus dem Dilemma nachzudenken.
Selbst “statement_timeout” liefert eine Fehlermeldung zurück, wenn das eine Anfrage abbricht. Du musst also deinen Code umschreiben und darauf entsprechend reagieren.
Ist also jetzt schon Anpassungsarbeit notwendig. Jetzt ist also ein guter Zeitpunkt, das mal richtig zu machen: Zukunftstechnologien, Internet, und so :wink:

Im Gegenteil, das Schöne an dieser Lösung ist ja - und deswegen habe ich danach gefragt - dass ich nun doch den alten Ifx-Code nehmen kann und erst einmal gar nichts umbauen muss. Die Ifx-Variante fängt ja genau diesen Timeout-Fehler bereits ab, also hält sich der Aufwand für mich in Grenzen. Das Zeug ist schon in Stein gemeißelt, denn an sich wird es nicht mehr weiterentwickelt. Die neuen Maschinen haben alle schon Internet, aber die (ca. 1000) weltweit verteilten Teile eben noch nicht, und die sollen noch ein paar Jährchen laufen. Die Datenbanken werden aber jetzt schon auf PG umgestellt, also muss der alte “Read-Only-Code” nun auch diese Syntax verstehen. Es sollen aber, um Aufwand und Kosten zu minimieren und die Kompatibilität mit der bisherigen Software zu garantieren, nur die SQL-Statements angefasst werden. Das könnte mit dem statement_timeout tatsächlich klappen. Ihr seid die Ersten, die erfahren, wenn es nicht funktioniert g !

Das fürchte ich :wink: