第 45章バックグラウンドワーカプロセス

PostgreSQLはユーザ提供のコードを別のプロセスとして実行できるように拡張することができます。 このプロセスはpostgresによって起動、終了、監視され、サーバの状態に密接にリンクしています。 これらのプロセスはPostgreSQLの共有メモリ領域にアタッチしたり、データベースの内部に接続するオプションを持ちます。これらはまた、通常のクライアントに接続された実際のサーバプロセスのように複数のトランザクションを連続して実行することができます。また、アプリケーションはlibpqとリンクすることにより通常のクライアントアプリケーションのようにサーバに接続して動作することができます。

警告

バックグラウンドワーカを使うにあたっては、堅牢性とセキュリティリスクを考慮しなくてはなりません。なぜならば、C言語で書かれており、データへのアクセスが制限されていないためです。バックグラウンドワーカプロセスを有効にしたいと思っている管理者は厳重に注意して実践すべきです。注意深く検査されたモジュールだけが、バックグラウンドワーカプロセスの実行を許されるべきです。

バックグラウンドワーカはPostgreSQLスタート時にモジュールを shared_preload_libraries に記すことによって実行できます。バックグラウンドワーカとして実行したいモジュールは RegisterBackgroundWorker (BackgroundWorker *worker) _PG_init() から呼び出して登録する必要があります。バックグラウンドワーカはシステム起動後も RegisterDynamicBackgroundWorker (BackgroundWorker *worker, BackgroundWorkerHandle **handle) を呼び出すことによって実行することができます。RegisterBackgroundWorker とは異なり、postmasterからのみ呼び出すことができます。 RegisterDynamicBackgroundWorker は通常のバックエンドから呼び出す必要があります。

その構造、BackgroundWorker は以下のように定義されます。

typedef void (*bgworker_main_type)(Datum main_arg);
typedef struct BackgroundWorker
{
    char        bgw_name[BGW_MAXLEN];
    int         bgw_flags;
    BgWorkerStartTime bgw_start_time;
    int         bgw_restart_time;       /* 秒単位、もしくは、BGW_NEVER_RESTART */
    bgworker_main_type bgw_main;
    char        bgw_library_name[BGW_MAXLEN];   /* bgw_mainがNULLの場合のみ */
    char        bgw_function_name[BGW_MAXLEN];  /* bgw_mainがNULLの場合のみ */
    Datum       bgw_main_arg;
    int         bgw_notify_pid;
} BackgroundWorker;

bgw_name は、ログメッセージ、プロセス一覧等で使用される文字列です。

bgw_flags は、モジュールが要求する機能をOR演算したビットマスクです。可能な値は BGWORKER_SHMEM_ACCESS (共有メモリへのアクセスを要求)と BGWORKER_BACKEND_DATABASE_CONNECTION (データベース接続を確立し、その後トランザクションやクエリの実行が可能です)。データベースに接続するために BGWORKER_BACKEND_DATABASE_CONNECTION を使っているバックグラウンドワーカもまた、 BGWORKER_SHMEM_ACCESS を使って共有メモリに接続しなければなりません、さもないとワーカの起動に失敗します。

bgw_start_time は、 postgres がプロセスを起動するべきタイミングを指定します。そのタイミングは、 BgWorkerStart_PostmasterStart (postgres自身が初期化を終えるとすぐに起動します。これを要求するプロセスはデータベース接続に望ましいものではありません。)、 BgWorkerStart_ConsistentState (ホットスタンバイで一貫性のある状態に到達すると、つまりデータベースに接続して参照のみのクエリを許可する状態になると起動します。)、 BgWorkerStart_RecoveryFinished (システムが参照/更新クエリを実行すると起動します。)のうちの一つです。この設定はいつプロセスが起動されるかを示すだけであることに注意してください。これらのプロセスは、違う状態になったときに停止するわけではありません。

bgw_restart_time は、プロセスがクラッシュした場合に postgres がそのプロセスを再起動するために待つべき間隔を秒単位で指定します。これは任意の正の値、またはクラッシュしても再起動しない場合は BGW_NEVER_RESTART を指定します。

bgw_main は、プロセスが起動されたときに実行される関数へのポインタです。この関数はDatum型の引数を一つとり、 void を返さなければなりません。 bgw_main_arg は、唯一の引数として渡されます。グローバル変数である MyBgworkerEntry は、登録時に渡される BackgroundWorker 構造体のコピーを指すことに注意してください。bgw_main は NULL であってもよく、その場合は bgw_library_namebgw_function_name がエントリーポイントを決定するために使用されます。これはpostmater起動時にはpostmasterが必要なライブラリがロードされていないときにバックグラウンドワーカを後で立ち上げるときに有用です。

bgw_library_name はバックグラウンドワーカの初期エントリーポイントのためのライブラリ名です。 bgw_main がNULLでない限りそれは無視されます。 bgw_main がNULLの場合、ワーカプロセスにより動的にロードされるライブラリ名です。 bgw_function_name は呼び出される関数名を識別するために使用されます。

bgw_function_name は新しいバックグラウンドワーカから動的にロードされるときに初期エントリーポイントの関数名です。それは bgw_main がNULLのとき無視されます。

bgw_notify_pid はプロセスの開始時と終了時にpostmasterにSIGUSR1を送信するPostgreSQLバックエンドプロセスのPIDです。それはpostmasterの起動時間、またはワーカが起動するのを待ちたくない場合に0である必要があります。それ以外の場合は、 MyProcPid で初期化すべきです。

ひとたび実行すると、このプロセスは BackgroundWorkerInitializeConnection( char *dbnamechar *username ) を呼び出すことによって、データベースに接続できます。 これはプロセスに SPI を使用してのトランザクションとクエリの実行を許します。もし、 dbname がNULLであった場合、そのセッションは特定のデータベースに接続しません。しかし、共有カタログにはアクセスできます。もし、 username がNULLの場合、そのプロセスは initdb 時に作成されたスーパーユーザとして実行されます。 BackgroundWorkerInitializeConnectionは、バックグラウンドワーカごとに一度のみ呼ぶことができ、これはデータベースを切り替えることができません。

制御が bgw_main 関数に達したとき、シグナルはまずブロックされます。このブロックは解除されなければなりません。このことは、必要があれば、プロセスにそのシグナルハンドラをカスタマイズできることを意味します。シグナルは新しいプロセスで BackgroundWorkerUnblockSignals を呼び出すことにより、解除でき、 BackgroundWorkerBlockSignals を呼び出すことでブロックできます。

バックグラウンドワーカは bgw_restart_timeBGW_NEVER_RESTART に設定されている場合、または TerminateBackgroundWorker によって0で終了した場合、postmasterに自動的に登録が解除されて終了します。 それ以外の場合、 bgw_restart_time で設定された時間の後に再起動します。または、バックエンドの障害のために posmasterが最初期化された場合はすぐに再起動します。 バックグラウンドワーカが何もすることがないときは、割り込み可能な休止状態にすることを考慮しないといけません。これは WaitLatch() を呼び出すことによって可能になります。 この関数を呼んだときに、 WL_POSTMASTER_DEATH フラグが設定され、 postgres 自身が終了する緊急事態には、リターンコードを確認するようにしてください。

バックグラウンドワーカを RegisterDynamicBackgroundWorker 関数により登録している場合、登録を実行するバックエンドはワーカの状態に関する情報を取得することが可能です。取得したい場合は RegisterDynamicBackgroundWorkerBackgroundWorkerHandle * のアドレスを渡す必要があります。もし登録に成功した場合、 GetBackgroundWorkerPid (BackgroundWorkerHandle *pid_t *) 、または TerminateBackgroundWorker (BackgroundWorkerHandle *) のポインタは初期化されます。 GetBackgroundWorkerPid はワーカの状態を監視することができます。以下の返り値が得られます。 BGWH_NOT_YET_STARTED ワーカはまだpostmasterにより開始されていない。BGWH_STOPPED開始されたが、もはや実行されていない。 BGWH_STARTED実行中です。この最後のケースでは、PIDは、第二の引数を介して返されます。 TerminateBackgroundWorkerはワーカが実行していた場合postmasterがワーカに SIGTERM を送信し、まもなく登録を解除します。

場合によっては、バックグラウンドワーカを登録する処理は、ワーカが起動するのを待つことをお勧めします。これは bgw_notify_pid から MyProcPid で初期化し登録時に得られた BackgroundWorkerHandle * を使用して WaitForBackgroundWorkerStartup (BackgroundWorkerHandle *handlepid_t *) 関数を呼び出すことで実現します。 postmasterがバックグラウンドワーカを開始しようと試みたか、postmasterが死ぬまで、この関数はブロックします。バックグラウンドランナーが実行されている場合、戻り値は BGWH_STARTED 、およびPIDが提供されたアドレスに書き込まれます。そうでない場合、戻り値は BGWH_STOPPED または BGWH_POSTMASTER_DIED になります。

バックグラウンドワーカの例として、contribに worker_spi というモジュールがあります。これはいくつかの有用な技術を示しています。

登録できるバックグラウンドワーカの数は max_worker_processesによって制限されています。