Dienstag, Juni 17, 2014

Autonomes Logging mit postgres

Im Rahmen von ETL-Operationen ergibt sich immer wieder die Situation, dass man eine Log-Message in ein ETL-Log schreiben möchte, ohne dadurch die eigentliche Transaktion per commit festzuschreiben, denn der Umfang der Transaktionen sollte nicht durch die Log-Einträge bestimmt werden. Da die Log-Nachricht im Fall eines Rollbacks der Transaktion ebenfalls zurückgerollt werden würde, kann man sich allerdings auch nicht auf den Abschluss der eigentlichen ETL-Operation verlassen. Zur Lösung dieses Problems bietet Oracle das Pragma Autonomous Transaction an, mit dessen Hilfe man eine untergeordnete Transaktion unabhängig von der rahmenden Transaktion abschließen kann.

Leider bietet postgres kein entsprechendes Feature an, so dass man sich dort mit einem Workaround - man könnte auch sagen "horrible hack" - behelfen muss: man schreibt die Log-Informationen über einen loopback DB-Link in eine Log-Tabelle; und da der Zugriff via DB-Link außerhalb der Transaktion liegt, führt ein Rollback der rahmenden Transaktion nicht mehr zum Rollback der Log-Nachricht:

postgres=# create extension dblink;
CREATE EXTENSION

postgres=# create table log_table(message text);
CREATE TABLE

postgres$# begin
postgres$#     perform dblink_connect('log_link','dbname=postgres');
postgres$#     perform dblink_exec('log_link','insert into log_table(message) values (''' || message || ''');');
postgres$#     perform dblink_exec('log_link','commit;');
postgres$#     perform dblink_disconnect('log_link');
postgres$# end;
postgres$# $$
postgres-# language plpgsql
postgres-# ;
CREATE FUNCTION

postgres=# select insert_log_message('123');
 insert_log_message
--------------------
(1 Zeile)

postgres=# select * from log_table;
 message
---------
 123
(1 Zeile)

Und jetzt noch der Beweis, dass eine auf diesem Weg aufgerufene Sub-Transaktion tatsächlich das Rollback der rahmenden Transaktion überlebt:

-- fehlerhafter Code
postgres=# create or replace function test_transaction_handling() returns void
postgres-# as
postgres-# $body$
postgres$#
postgres$# begin
postgres$#
postgres$# perform insert_log_message('ueberlebt rollback');
postgres$#
postgres$# execute 'select ,,,';
postgres$#
postgres$# end
postgres$# $body$
postgres-# LANGUAGE plpgsql
postgres-# ;
CREATE FUNCTION

-- Aufruf mit Fehler
postgres=# select test_transaction_handling();
ERROR:  syntax error at or near ","
ZEILE 1: select ,,,
                ^
ANFRAGE:  select ,,,
KONTEXT:  PL/pgSQL function test_transaction_handling() line 7 at EXECUTE statement

-- aber die Log-Nachricht wurde geschrieben:
postgres=# select * from log_table;
      message
--------------------
 123
 ueberlebt rollback

Schön ist das natürlich nicht, aber es funktioniert. Allerdings würde ich Neil Conway zustimmen, der auf den Vorschlag dieses Hacks im postgres-Forum antwortete:
I agree that autonomous transactions would be useful, but doing them via dblink is a kludge. If we're going to include anything in the core database, it should be done properly (i.e. as an extension to the existing transaction system).
Bis zur Ergänzung des Features (oder sonst einer besseren Lösung) bin ich mit diesem "Workaround" aber erst mal zufrieden. 

Keine Kommentare:

Kommentar veröffentlichen