2012-08-15 6 views
9

Ich habe eine C# -Client-Anwendung, die Npgsql verwendet, um eine plpgsql-Funktion in PostgreSQL 9.1.4 aufrufen. Die Funktion benötigt sehr viel Zeit und ich möchte dem Kunden auf gewisse Weise Fortschritte mitteilen. Wie soll ich das machen?Wie melden Fortschritt von lang laufenden PostgreSQL-Funktion zu Client

Der LISTEN/NOTIFY-Mechanismus klingt perfekt dafür, außer dass das Ganze innerhalb einer Transaktion läuft und NOTIFY-Ereignisse erst am Ende der Transaktion gesendet werden, was für mich nutzlos ist.

Die andere Sache, die ich ausprobiert habe, ist RAISE NOTICE, die ich auf dem Client verarbeiten kann, aber selbst diese Benachrichtigungen scheinen für eine Weile gepuffert und in Stapeln gesendet zu werden. Es ist besser als nichts, aber nicht ideal. Gibt es eine Möglichkeit, sie zu "spülen", so dass sie sofort an den Kunden gesendet werden?

Antwort

6

Es gibt nichts Besseres als RAISE NOTICE.

Diese Signale werden nicht gepuffert - und sie sind asynchron - Sie haben wahrscheinlich Probleme bei der Nachrichtenverarbeitung in Ihrer Anwendung.

+0

Mit PostgreSQL 8.4 und höher können Sie auch einen Fehlercode an RAISE NOTICE liefern, der es einfacher macht, den Unterschied zwischen Fortschrittsnachrichten und anderen Mitteilungen, die ausgegeben werden können, zu unterscheiden. –

+0

Sie haben recht, es stellt sich heraus, dass die NOTIZEN * geliefert wurden, als sie ausgelöst wurden, aber es gab eine merkwürdige Verlangsamung in der Funktion, was nicht passierte, wenn ich dieselbe Abfrage direkt ausführte. Ich habe jetzt einen Workaround dafür gefunden. – EM0

+0

RAISE NOTICE hat einen relativ hohen Overhead - es ist ein Netzwerk-Handshake zwischen Client und Server. Normalerweise merke ich nicht jede Iteration, merke aber jede tausend Iteration. –

1

Der einfachste Weg wäre, Ihre Pgsql-Funktion in mehrere Unterfunktionen aufzuteilen, als sie sequenziell auf der Anwendungsseite aufzurufen und den Transaktionsbereich in der Anwendung zu verwalten.

+0

Danke, aber in diesem speziellen Fall wäre es nicht einfach, das kann ich Ihnen versichern. :) Irgendwelche anderen Möglichkeiten? – EM0

+0

Nicht dass ich daran denken kann, sorry – mathieu

7

Neben @ Pavels exzellentem Punkt über RAISE NOTICE gibt es noch eine klassische Technik, um den Abfragefortschritt in Pg zu überwachen. Es ist ein bisschen wie ein Hack, aber es ist ziemlich effektiv.

Sie können die Tatsache ausnutzen, dass Änderungen an Sequenzen sofort überall sichtbar sind, um den Fortschritt einer Funktion nach außen sichtbar zu machen. Verwenden Sie entweder eine fest codierte Sequenz und stellen Sie sicher, dass die Funktion nicht gleichzeitig aufgerufen wird, oder übergeben Sie den Namen der Fortschrittsüberwachungssequenz an die Funktion.

Ihre Funktion kann bei jeder Iteration nextval(seqname) aufrufen, und interessierte Parteien können den Status der Sequenz mit SELECT last_value FROM seqname aus einer anderen Sitzung untersuchen.

Sie können die Reihenfolge einen Countdown-bis zur Fertigstellung machen, indem Sie sich mit

create sequence test maxvalue 2147483647 increment by -1; 

und riefen setval('seqname', num_items) zu Beginn Ihrer Funktion einstellen. Sie zählt dann mit jedem Aufruf nextval in Richtung Null. 2147483647 ist maxint, übrigens.

Es ist unnötig zu sagen, dass dies nicht tragbar ist, und es gibt keine Garantie, dass SELECT einer Sequenz immer auf diese Weise funktioniert. Es ist verdammt praktisch.

+0

+ 1 Netter Trick, danke. – EM0

0

Sie können auch verwenden:

EXECUTE 'COPY (SELECT ''progress: ' || progress_variable || ''') TO ''d:\progress.txt'''; 

in Ihrer Funktion den aktuellen Fortschritt in eine Textdatei zu schreiben.