Help: Großes Problem mit insert/select - Daten werden nicht runtergeschrieben

Hi,

vermutlich liegt es nur an einer Einstellung, habe folgendes Problem:

Auf einem SQL-Handle wir eine längere Transaktion abgwickelt, in der mehrere Selects und dann ein Insert abgesetzt werden bevor commited wird.

z.B. folgende Sequenz

insert into branche (brancheinr, bez, rowversion) values (22, ‘Testbranche2’, null);

select rowversion from branche where brancheinr = 22;

commit;

Nachdem das Insert abgesetzt wurde muß ich unmittelbar als nächstes Statement die akt. Rowversion selektieren (wir von einem Row-Trigger before insert generiert (wenn Rowversion = null -> 1, sonst Rowversion = Rowversion + 1).

Dieses Select geht ins leere, der Datensatz ist nicht wirklich gespeichert. Das nachfolgendse commit speichrt auch nicht den Datensatz er geht irgendwie verloren.

Ich kann aber kein commit absetzen bevor ich die Rowversion nicht hab.

Bei einer Sequenz z.B.

Insert …;
commit;

wird der Datensatz gespeichert.


Meine Frage: Wo könnte der Datensatz verlorengehen? Hängt das mit irgendwelchen Einstellungen zusammen? Irgendwelchen Plattencaches? Irgendwelchen fsm/wal einstellungen?

habe
fsync auf on,
wal_sync_method = open_datasync


Ich hoffe, daß das nur eine Einstellungssache ist, sonst ist die Postgresql als Datenbank unbrauchbar.


Grß
Hans






Nachdem das Inser

Bahnhof.

Prüfst du, ob alle Anfragen richtig funktionieren oder ob es einen Fehler gibt? Taucht etwas im Logfile auf?

So, und in deiner Reihenfolge blicke ich leider nicht ganz durch. Kannst du mal Schritt für Schritt beschreiben, was passiert (bzw. passieren soll)?

Du fügst NULL als rowversion ein und erwartest anschließend da einen Wert. Wie kommst Du auf solch dünnes Eis?


Andreas

Okay, ich hab in meiner ersten Antwort den Trigger übersehen, sorry.

Kann es sein, daß da dann was schief läuft? Sowas wie Vergleich auf NULL via = oder sowas?


Andreas

Wie ich solche präzisen und fachlich fundierten Statements liebe :slight_smile:
Unbrauchbar im Vergleich zu ???

Aber naja:
Es wäre toll wenn du
1.) Den Code in deiner App
2.) ein Postgres log (debug5)

pasten könntest, vermutlich wird die unbrachbare Datenbank Dir einen Berg von Fehlermeldungen um die Ohren hauen :slight_smile:

habe in der Zwischenzeit einige Details zusammengetragen:

Die App legt eine Auftragsposition an und muß dabei auch in anderen Tabellen Daten updaten, inserten (z.B. Mengen und Reservierungen in der Lagertabelle, Verfügbarkeiten etc.)

Vorgangsweise:

  1. hole über die sequenz einen PK

  2. 2 Phasen commit einleiten

  3. SQL Prepare

  4. Insert (der Datensatz ist vorerst noch da, aber im laufe von punkt 4 (transaktionen über einen anderen handle) ist er plötzlich weg)

  5. Nebentätigkeiten in anderen Tabellen (diese laufen in einer eigenen Arbeitsklasse Lagebuchung ab. Diese benutzt dazu eigene sql handles. D.h. diese transaktionen laufen selbständig ab. Wenn von hier ein true zurückkommt, dann wird in der Auftragsposition commited.

  6. commit (dazu habe ich 2 Phasen commit aufgebaut, im Programmcode wird auch das ‘commit prepared …’ aufgerufen, aber im logfile hab ich keinen Eintrag dazu gefunden)

Was passiert eigentlich wenn ich mit z.B. 2 sql handles arbeite und auf beiden transaktionen mit inserts/updates habe und auf einem handle ein commit absetze? Wird die transaktion auf dem zweiten handle auch commited?

Was ich im logfile bemerkt habe, schickt mein client vor den sqlstatements immer ein BEGIN; mit. Ob das das Problem verursacht?


Programmcode auszugsweise:

  1. Call HoleNextIVNR(SysTABGetDBHauptTabelle(), SysTABGetPK(), nPrimaryKey)

  2. Call SqlPrepareAndExecute(hSql1, “prepare transaction 'insert_row_” || SalNumberToStrX(nPrimaryKey,0) || “’”)

  3. Set bErg = SqlPrepare( hSql1, sInsert )
    Call SalSendMsgToChildren(hWndForm, SM_SPALTE_LongStringFremdeDB, 2, 0)

  4. Set bErg = SqlExecute( hSql1 )

  5. ! hier werden transaktionsbedingte Aktionen in anderen Tabellen durchgeführt.
    Set bErg = hWndTB.clsfrmButton5…KontrolleNeuanlage( )

  6. Call SqlPrepareAndExecute(hSql1, “commit prepared 'insert_row_” || SalNumberToStrX(nPrimaryKey,0) || “’”)



    Das Logfile habe ich angefügt

Herzliche Grüße
Hans

Also, ich sehe nach den Insert Befehlen jeweils ROLLBACK Statements, keine COMMITs. Dann ist natürlich klar, warum die Daten nie in der Datenbank ankommen.

aber wo kommen die rollbacks her? Im der app werden sie ganz sicher nicht abgesetzt, vor allem nicht auf dem Handle wo ich das insert absetze. Kann es sein, daß bei einer sequenz wie z.b.

connect handle(sh1…)
select …
disconnect handle(sh1)

automatisch ein rollback passiert, wenn nach dem select statement kein commit kommt?

Und wenn ja, wieso bezieht sich dann dieses rollback auf meinen insert handle?

Bei einem 2 Phasen commit darf das doch nicht zusammenhängen oder?

Frohe Weihnachten
Hans

Wenn du eine Transaktion offen hast und die Verbindung beendest, wird alles zurückgerollt: du hast schliesslich die Änderungen nicht durch ein COMMIT bestätigt.

Woher die Rollbacks sonst kommen, kann ich dir leider nicht sagen, die Datenbank wird sie aber nur schwer selber erzeugen. Ist vielleicht an der Zeit, herauszufinden, was dein Programm dort wirklich tut …

Also, mit 2PC hab ich selber exakt Null Erfahrung, aber: ein ROLLBACK ist ein Rollback. Und so, wie ich 2PC verstanden habe, ist zu einem erfolgreichem COMMIT ein COMMIT aller beteiligten Verbindungen zwingend nötig. Von daher wundert es mich nicht. Und ja, eine nicht durch COMMIT abgeschlossene Verbindung erzwingt ein ROLLBACK. PG ist nicht so frech ein COMMIT zu vermuten an dieser Stelle. Sowas macht eher MySQL bei Masseninserts, wo es ab und an von selbst ein COMMIT einwirft…

Andreas

folgendes konnte ich genauer eingrenzen:

der 2te handle auf dem selektionen gemacht werden, wird connected und bleibt solange bestehen, bis der applikationsteil (auftragspositionen bearbeiten) beendet wird. d.h. ein rollback durch das disconnect eines handles kann definitiv ausgeschlossen werden.

was mich stutzig macht, ist das rollback bei einem selectstatement. man beachte in dem logfilefragment die ersten beiden select statements auf die das rollback folgt.

diese werden von der applikation aber nicht so übergeben, sondern genau so, wie die sieben folgenden select statements im log. d.h. lagerinr = 1577 und nicht NULL!!! (siehe log weiter unten)

woher kommen die beiden falschen statements die ganz offensichtlich das rollback auslösen?

Gruß
Hans


2007-12-24 09:25:09 DEBUG: StartTransactionCommand
2007-12-24 09:25:09 STATEMENT: select coalesce(is_mengenrechnung,0) from sysadm.lager, sysadm.produktart where lagerinr = NULL and prodartbez=bez
2007-12-24 09:25:09 DEBUG: CommitTransactionCommand
2007-12-24 09:25:09 STATEMENT: select coalesce(is_mengenrechnung,0) from sysadm.lager, sysadm.produktart where lagerinr = NULL and prodartbez=bez
2007-12-24 09:25:09 DEBUG: StartTransactionCommand
2007-12-24 09:25:09 STATEMENT: ROLLBACK
2007-12-24 09:25:09 DEBUG: ProcessUtility
2007-12-24 09:25:09 STATEMENT: ROLLBACK
2007-12-24 09:25:09 DEBUG: CommitTransactionCommand
2007-12-24 09:25:09 STATEMENT: ROLLBACK
2007-12-24 09:25:09 DEBUG: StartTransactionCommand
2007-12-24 09:25:09 STATEMENT: BEGIN;select coalesce(is_mengenrechnung,0) from sysadm.lager, sysadm.produktart where lagerinr = ‘1577’::float8 and prodartbez=bez
2007-12-24 09:25:09 DEBUG: StartTransaction
2007-12-24 09:25:09 STATEMENT: BEGIN;select coalesce(is_mengenrechnung,0) from sysadm.lager, sysadm.produktart where lagerinr = ‘1577’::float8 and prodartbez=bez
2007-12-24 09:25:09 DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 217740/1/0, nestlvl: 1, children: <>
2007-12-24 09:25:09 STATEMENT: BEGIN;select coalesce(is_mengenrechnung,0) from sysadm.lager, sysadm.produktart where lagerinr = ‘1577’::float8 and prodartbez=bez
2007-12-24 09:25:09 DEBUG: ProcessUtility
2007-12-24 09:25:09 STATEMENT: BEGIN;select coalesce(is_mengenrechnung,0) from sysadm.lager, sysadm.produktart where lagerinr = ‘1577’::float8 and prodartbez=bez
2007-12-24 09:25:09 DEBUG: CommitTransactionCommand
2007-12-24 09:25:09 STATEMENT: BEGIN;select coalesce(is_mengenrechnung,0) from sysadm.lager, sysadm.produktart where lagerinr = ‘1577’::float8 and prodartbez=bez
2007-12-24 09:25:09 DEBUG: StartTransactionCommand
2007-12-24 09:25:09 STATEMENT: BEGIN;select coalesce(is_mengenrechnung,0) from sysadm.lager, sysadm.produktart where lagerinr = ‘1577’::float8 and prodartbez=bez
2007-12-24 09:25:09 DEBUG: CommitTransactionCommand
2007-12-24 09:25:09 STATEMENT: BEGIN;select coalesce(is_mengenrechnung,0) from sysadm.lager, sysadm.produktart where lagerinr = ‘1577’::float8 and prodartbez=bez
2007-12-24 09:25:09 DEBUG: StartTransactionCommand

Ähm, Tschuldigung, die Datenbank erzeugt die Anfragen nicht von sich aus … das wird irgendwo in deiner Anwendung passieren.

Ach, was mir gerade auffällt: das müsste

lagerinr IS NULL

heissen. Sieht also so aus, als ob die Anwendung irgendwo fehlerhaft einen Query zusammensetzt und den dann ausführt.

Hallo ads,

das Problem ist gelöst. Ursache war: Ein Datenbankparameter ‘ROLLBACKONTIMEOUT’ war auf true und 30 Sekunden gesetzt, damit wurde der handle über den ich hilfsstatements abgesetzt habe auf timeout gesetzt und bei benutzung gabs ein rollback.

Das prepare mit den NULLs scheint ein standardverhalten zu sein (weiß abr nicht ob von der db oder von odbc) und stört nicht weiter. es sieht so aus als ob zu dem zeitpunkt des prepares die bindvariablen noch nicht verfügbar sind und daher mit NULL vorbelegt werden um dann scheinbar beim execute mit den bindinhalten ersetzt zu werden.

Danke für die Hilfe
Hans

Und so zerbröselt der Keks nun mal :wink:

ich habe da noch was festgestellt, das backend macht einen unterschied zwischen notationen im whereteil und zwar sowohl bei den selects, updates und deletes.


z.b. variante 1
select … from … where aufposinr = :nAufposinr (das dauert ca. 3 sekunden)

und variante 2
select … from … where aufposinr = 35278 (dauert weniger als eine halbe sekunde)

gibt es einstellungsmöglichkeiten mit denen man das verhalten von variante 1 beschleunigen kann? ich möchte mir
den umbau in der app auf variante 2 nach möglichkeit ersparen.

herzliche grüße
Hans

Wo genau kommen die Daten in deiner ersten Anfrage her? In der zweiten werden die Werte (die 35278) ja direkt eingesetzt.

Ansonsten: ein EXPLAIN und EXPLAIN ANALYZE ist dein Freund, das sagt dir genau, warum deine Anfrage langsam ist.

beide varianten kommen aus der applikation. variante 1 war der ursrüngliche programmcode. variante 2 ist die abänderung die performancegewinn bringt (in der app werden die sqlstatements dynamisch erzeugt, bei variante 2 hänge ich statt der bindvarible direkt den wert aus der bindvariable ans statement - wäre ein irrer aufwand die app darauf umzustellen).

explain und explain analyze bringen immer super werte, weil ich bei diesen statements hauptsächlich den pk verwende und damit wird auch der unique index immer angesprochen. die db macht ihren job, aber es dauert bis die app was zurückkriegt.

deshalb vermute ich, dass der odbctreiber zeit verliert. der pgAdminIII greift ja nicht über odbc sondern irgendwie direkt auf die db zu.

die odbc-schnittstelle hat scheinbar viel overhead zu bewältigen und wird dadurch zäh und langsam.

ich möchte unbedingt die postgresql einsetzen, weil sie wie ich zuletzt gesehen habe eine wirklich gute db ist. das wird doch nicht an einer odbc-schnittstelle scheitern.

eine ausweichlüberlegung (ist ziemlich weit her geholt): wie weit kann die pg die oracle wirklich ersetzen? mein devtool kann nämlich die oracle native ansprechen - d.h dann könnte man man ev. über die oracle schnittstelle in die pg hinein? …kennt jemand eine derartige migration oracle to pg?

Also ob du PG über eine Oracle Schnittstelle direkt ansprechen kannst … eher nicht, dafür müssten die Bibliotheken ja binär kompatibel sein.

Ich weiss aber, was du machen kannst: du kannst mal etwas Debugging anschalten und schauen, wo genau wieviel Zeit verbraucht wird. PostgreSQL kann dir das sehr genau mitteilen.

Danach kannst du ev. den Leuten, die den ODBC Treiber geschrieben haben, mal auf den Finger hauen … oder auch den PostgreSQL Leuten, falls es an der Datenbank liegt.

Hallo Hans,

ODBC ist eine standardisierte Schnittstelle für den Datentransfer zwischen Applikation und Datenbank und umgekehrt. Wie die Daten über ODBC transferiert werden bestimmt immer die Applikation. Auch wenn pgAdmin die Daten nicht über ODBC tranferiert, kann pgAdmin sehr hilfreich sein. pgAdmin auf dem Datenbankserver installiert und den SQL-Befehl im Query-Dialog ausgeführt liefert in der Statusleiste eine Zeitangabe, die man als Min-Zeit für die Ausführung des SQL-Befehls annehmen kann. Die Zeitdifferenz wird dann von der Applikation und den Transport über das Netzwerk benötigt. Dass es nicht an ODBC liegt, beschreibt die 2. Variante. Die Zeitdifferenz zwischen Variante 1 und Variante 2 ergibt sich daher durch die unterschiedliche Ausführung des SQL-Befehls in der Applikation. Dieses sollte untersucht werden. Hilfreich könnte da auch ein Blick ins Internet sein.

Wie eigenartig eine Applikation ein SQL-Befehl auslegen kann, kann man hier nachlesen.





mfg

fristo