web-dev-qa-db-ja.com

PostgreSQLトランザクション内からカスタムメッセージを記録する方法

トランザクションを生成して実行するソフトウェアがあります。クエリの総数に対して実行されたクエリの単純な割合という観点から、トランザクションの進行状況を非同期で検査したいと思います。私はstdoutによって生成されたstderrpsqlの両方を読み取ることができるので、ソリューションがstdoutのようないくつかのカスタムメッセージに出力できると考えていました"進捗状況:3/8"(または別のカスタムテキスト文字列)。

クエリの進行状況に関する情報を適切なテーブルに保存する「ロギングクエリ」を実行することを考えましたが、このテーブルはトランザクションが完了するまで利用できず、トランザクションの進行状況を調べるのに役立ちません。

現時点では、次のことを試しました(トランザクションに3つのクエリがあり、何らかの処理を行うと仮定します)。

_BEGIN;
CREATE OR REPLACE FUNCTION progress(curr int, total int) RETURNS float AS $$
BEGIN
  RAISE NOTICE '___PROGRESS___%', curr/total::float;
  RETURN curr/total::float;
END;

-- First query
SELECT ... FROM ...;
-- Log the progress
SELECT * FROM progress(1,3);

-- Second query
SELECT ... FROM ...;
-- Log progress
SELECT * FROM progress(2,3);

-- Third query
SELECT ... FROM ...;
-- Log progress
SELECT * FROM progress(3,3);

COMMIT;
_

次の構文を使用して、bashファイルからスクリプトを実行します。

_res=$((psql -U username -d db -h localhost --log-file=plog.log -f "the transaction above") 2>&1>clog.log)
_

前のbashコマンドを使用して上記のようなトランザクションから取得するのは、_plog.log_、_clog.log_および_$res_ bash変数の3つの出力です。それらの抜粋を投稿して、内容が明確になるようにします。

plog.log

_....
********* QUERY **********
SELECT * from progress(1, 3);
**************************

     progress      
-------------------
 0.333333333333333
(1 row)

...
********* QUERY **********
SELECT * from progress(2, 3);
**************************

     progress      
-------------------
 0.6666666666666667
(1 row)

...
********* QUERY **********
SELECT * from progress(3, 3);
**************************

     progress      
-------------------
 1
(1 row)
_

clog.log

_...
     progress      
-------------------
 0.333333333333333
(1 row)

...
     progress      
-------------------
 0.6666666666666667
(1 row)

...
     progress      
-------------------
 1
(1 row)    
_

_$res_変数(psql戻り値)

_psql:/path/to/script.sql:11: NOTICE:  ___PROGRESS___0.333333333333333
psql:/path/to/script.sql:16: NOTICE:  ___PROGRESS___0.666666666666667
psql:/path/to/script.sql:21: NOTICE:  ___PROGRESS___1
_

問題は、_$res_変数の内容にリアルタイムでアクセスできないことです。代わりに_clog.log_および_plog.log_のコンテンツにアクセスできます。_RAISE NOTICE_と_clog.log_の両方に表示されないため、_plog.log_の使用はまったく意味がありません。 (SOMECODE)__、ただし_$res_のみ。

progress()関数からSELECT出力値を見つけるために、_clog.log_または_plog.log_を解析する必要があります(その中の_RAISE NOTICE_ステートメントを削除して)それは役に立たないために機能します)、または別の方法がありますか?何か不足していますか?

ありがとうございました

5
pietrop

RAISE NOTICEが正しい方法です。

RAISE NOTICEの出力がclog.logに表示されない理由は、標準エラーのリダイレクトがシェルレベルで正しくないためです。

あなたが欲しい:

psql --log-file=plog.log -f file.sql  >clog.log 2>&1

これにより、標準出力をclog.logにリダイレクトし、標準エラーをその標準出力にリダイレクトします。

それが質問で行われる方法:2>&1>clog.logは、bashマニュアルで説明されているように、間違った順序のためにそれを達成しません。

リダイレクトの順序は重要であることに注意してください。たとえば、次のコマンド

ls> dirlist 2>&1

標準出力(ファイル記述子1)と標準エラー(ファイル記述子2)の両方をファイルdirlistに送信し、コマンド

ls 2>&1> dirlist

標準出力がdirlistにリダイレクトされる前に標準エラーが標準出力のコピーになったため、標準出力のみをファイルdirlistに送信します。

3
Daniel Vérité

私たちのシステムでは、dblinkを使用して同じDBに書き込みます。これは、Oracle Autonomous Transactionの代替アプローチです。

ここでは、DBリンクの概念を使用して、ロギングの非同期コミットを実現しています。これは、Oracleの自律型トランザクションの概念と同じです。 dbリンク内で行われたコミットは、メイントランザクションでコミットを引き起こしません。

実際のデータは、コマンド "PERFORM PUBLIC.dblink_exec"を介してログテーブルに書き込まれ、すぐにコミットされます。

メインプロシージャがまだ実行中であるときに、別のセッションからログテーブルにクエリを実行することで、メインプロシージャの進行状況を追跡できます。何時間もの実行後にエラーが発生しても、ログデータはコミットされたままです。

#1: Confirm dblink extension

#2: Create DDLs
CREATE TABLE dblink_log (ID serial, message TEXT);--Table to store the logs

GRANT ALL ON TABLE core.dblink_log TO PUBLIC;--Permission to write as a dblink

GRANT ALL ON TABLE core.dblink_log_id_seq TO PUBLIC;

#3
/*Test block to write to a table and commit while 
  the main transaction is still in progress*/
DO$$ 
DECLARE
        open_connections TEXT [] := PUBLIC .dblink_get_connections () ; 
        --Cannot connect more than once before closing
        my_dblink_name TEXT := 'logging_dblink' ;
        --Define a name for your db link connection
    BEGIN

    --If the connection doest not exists, create one
    IF open_connections IS NULL 
       OR NOT open_connections @> ARRAY [ my_dblink_name ] THEN
        PERFORM PUBLIC .dblink_connect (
            my_dblink_name,
            'Host=127.0.0.1  port=5432 dbname=dbname user=user password=password'
        ) ; 
        raise notice 'New db link connection made' ;
    ELSE
        raise notice 'Db link connection exists, re-using' ;
    END IF ; 

    FOR i IN 1..10 loop 

        PERFORM PUBLIC.dblink_exec (
                my_dblink_name,
                'BEGIN;
                 INSERT INTO core.dblink_log (message) 
                 VALUES (''Executing loop #' || i || ''') ;
                 COMMIT;'
            ) ; 

        perform pg_sleep(10) ;-- Execute select * from core.dblink_log
                              -- in a different session
    END loop ;
END $$;

#4: From a different session verify record core.dblink_log is populated with data
0
Aneesh Mon N