特定のクエリがブロッカーになることを監視する関数を作成し(つまり、クエリによってブロックされ、別のクエリをブロックしている)、それを終了したいと思います。これが私の現在のコードです。クエリをブロックする必要がないように、テスト目的でleft join
を使用するように修正しました。
CREATE OR REPLACE FUNCTION monitor_sql_and_terminate_blocker(IN p_query text, OUT result text)
RETURNS text AS
$BODY$
DECLARE
monitor_sql text;
rec record;
BEGIN
monitor_sql = '
with blocker as
(
select distinct
waiting.pid pid
, other.pid blocker
from pg_stat_activity
join pg_catalog.pg_locks waiting
on waiting.pid = pg_stat_activity.procpid
and not waiting.granted
join pg_catalog.pg_locks other
on ( ( other."database" = waiting."database"
and other.relation = waiting.relation )
or other.transactionid = waiting.transactionid )
and other.pid <> waiting.pid
where current_query not like ''%<IDLE>%''
)
, blockers as
(
select pid, array_to_string(array_agg(blocker),'','') blocker_list
from blocker
group by pid
)
, blocking as
(
select blocker, array_to_string(array_agg(pid),'','') blocking_list
from blocker
group by blocker
)
select
procpid
from pg_stat_activity
left join blockers on blockers.pid = pg_stat_activity.procpid
left join blocking on blocking.blocker = pg_stat_activity.procpid
where current_query = ''' || p_query || '''
';
LOOP
FOR rec IN EXECUTE monitor_sql
LOOP
RAISE NOTICE 'Terminating procpid %', rec.procpid;
PERFORM pg_terminate_backend(rec.procpid);
END LOOP;
PERFORM pg_sleep(1);
END LOOP;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
これは次のように呼び出されます。
select monitor_sql_and_terminate_blocker('select * from very_large_table')
ただし、それは無限にループするだけで、何もしません。クエリを手動で実行すると、プロセスが検出されてprocpidが返され、手動で終了できます。
これはトランザクション分離のためであり、関数は開始時に実行されていたクエリのみを参照します。クエリの実行中に監視機能を実行すると、クエリは正常に強制終了され、その後何度も強制終了を試み続けます。それを回避するために何ができますか?
私の現在の解決策は、ループをpsqlを実行するシェルスクリプトに移動して、一度実行してから終了するこのコードのバージョンを呼び出すことです。
[元々、質問はPostgreSQLでタグ付けされていました。 Greenplumについてはわかりませんが、誰かがそのシステムに答えてくれることを願っています。残りはPostgreSQLにのみ適用されます(8.3以降、Greenplumは8.2からフォークされました)。]
ドキュメント に関連するスニペットがあります:
もう1つの重要な点は、サーバープロセスがこれらの統計のいずれかを表示するように求められると、最初にコレクタプロセスによって発行された最新のレポートをフェッチし、次に現在のトランザクションが終了するまで、すべての統計ビューと関数にこのスナップショットを使用し続けることです。 。
これは、一度クエリを実行したトランザクションの出力が凍結されることを意味しますpg_stat_activity
。幸いなことに、数文後、これは言われています:
または、pg_stat_clear_snapshot()を呼び出すこともできます。これにより、現在のトランザクションの統計スナップショット(存在する場合)が破棄されます。統計情報を次に使用すると、新しいスナップショットがフェッチされます。
他のセッションでさまざまなステートメントを発行しながら、以下の最小限の例で試してみました。
DO $$
DECLARE i text;
BEGIN
LOOP
PERFORM pg_stat_clear_snapshot(); -- makes the output up-to-date
PERFORM pg_sleep(1);
FOR i IN SELECT query FROM pg_stat_activity
LOOP
RAISE WARNING '%', i;
END LOOP;
END LOOP;
END;$$;
新しく発行されたクエリや新しいセッションも検出されるので、これが必要だと思います。