トランザクションを生成して実行するソフトウェアがあります。クエリの総数に対して実行されたクエリの単純な割合という観点から、トランザクションの進行状況を非同期で検査したいと思います。私はstdout
によって生成されたstderr
とpsql
の両方を読み取ることができるので、ソリューションが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つの出力です。それらの抜粋を投稿して、内容が明確になるようにします。
_....
********* 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)
_
_...
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
_ステートメントを削除して)それは役に立たないために機能します)、または別の方法がありますか?何か不足していますか?
ありがとうございました
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に送信します。
私たちのシステムでは、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