★PostgreSQLカンファレンス2024 12月6日開催/チケット販売中★
他のバージョンの文書 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

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

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

警告

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

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

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

typedef void (*bgworker_main_type)(Datum main_arg);
typedef struct BackgroundWorker
{
    char        bgw_name[BGW_MAXLEN];
    char        bgw_type[BGW_MAXLEN];
    int         bgw_flags;
    BgWorkerStartTime bgw_start_time;
    int         bgw_restart_time;       /* in seconds, or BGW_NEVER_RESTART */
    char        bgw_library_name[BGW_MAXLEN];
    char        bgw_function_name[BGW_MAXLEN];
    Datum       bgw_main_arg;
    char        bgw_extra[BGW_EXTRALEN];
    pid_t       bgw_notify_pid;
} BackgroundWorker;

bgw_namebgw_typeは、ログメッセージ、プロセス一覧、および同様の場面で使用される文字列です。 bgw_typeは、同じ種類のバックグラウンドワーカーで全て同じになるため、例えば同じ種類のワーカーをプロセス一覧でグループ化することができます。 一方でbgw_nameは、特定のプロセスに関する追加情報を含むことができます。 (通常、bgw_nameの文字列は何らかの形で種類に関する情報を含んでいますが、必須であるというわけではありません。)

bgw_flagsは、モジュールが要求する機能をOR演算したビットマスクです。可能な値は以下の通りです。

BGWORKER_SHMEM_ACCESS

共有メモリへのアクセスを要求します。 このフラグは必須です。

BGWORKER_BACKEND_DATABASE_CONNECTION

トランザクションやクエリの実行が出来るデータベース接続を要求します。 BGWORKER_BACKEND_DATABASE_CONNECTIONを使用してデータベースに接続するバックグラウンドワーカーはBGWORKER_SHMEM_ACCESSを使用して共有メモリにアタッチしなければなりません。さもなければ起動時に失敗します。

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

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

bgw_library_nameは、バックグラウンドワーカーの初期エントリポイントのためのライブラリ名です。 その指定されたライブラリがワーカープロセスによって動的にロードされます。呼び出すべき関数を特定するためにbgw_function_nameが使用されます。 コアコード内の関数を呼び出す場合、"postgres"を設定する必要があります。

bgw_function_nameは、新しいバックグラウンドワーカーの初期エントリポイントとして使用する関数の名前です。 この関数が動的にロードされたライブラリ内にある場合、PGDLLEXPORTstaticではない)とマークする必要があります。

bgw_main_argは、バックグラウンドワーカーのメイン関数のDatum引数です。 メイン関数は単一のDatum引数を取り、voidを返します。 bgw_main_argは引数として渡されます。 加えて、グローバル変数MyBgworkerEntryは、登録時に渡されたBackgroundWorker構造体のコピーを指しています。 ワーカーはこの構造を調べることがあり、役に立ちます。

Windowsの(どこか他の場所でEXEC_BACKENDが定義されている)場合、または動的バックグラウンドワーカーは、Datumを参照で渡すのは安全ではありません。値のみで渡してください。 引数が必要な場合は、int32型または他の小さな値を渡し、共有メモリに割り当てられた配列へのインデックスとしてそれを使用するのが最も安全です。 cstringtextのようなポインタを渡された場合は、新しいバックグラウンドワーカープロセスから有効になりません。

bgw_extraはバックグラウンドワーカーに渡す追加データを含めることが出来ます。 bgw_main_argとは異なり、このデータはワーカーのメイン関数の引数として渡されていませんが、上述したようにMyBgworkerEntryを介してアクセスすることが出来ます。

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

ひとたび実行すると、このプロセスはBackgroundWorkerInitializeConnection(char *dbname, char *username, uint32 flags)またはBackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, uint32 flags)を呼び出すことによって、データベースに接続できます。 これはプロセスにSPIインタフェースを使用してのトランザクションとクエリの実行を許します。 もし、dbnameがNULLであった場合、またはdboidInvalidOidであった場合には、そのセッションは特定のデータベースに接続しません。しかし、共有カタログにはアクセス出来ます。 もし、usernameがNULLの場合、またはuseroidInvalidOidの場合には、そのプロセスはinitdb時に作成されたスーパーユーザとして実行されます。 flagsとしてBGWORKER_BYPASS_ALLOWCONNが設定されている場合、データベースへ接続する際のユーザ接続を許可しない制約を回避することが出来ます。 バックグラウンドワーカーはこれら2つの関数をどちらかを一度だけ呼ぶことが出来ます。 データベースを切り替えることができません。

バックグラウンドワーカーのメイン関数に制御が達したとき、シグナルは最初にブロックされています。このブロックは解除されなければなりません。 これは、必要に応じてプロセスがシグナルハンドラをカスタマイズできるようにするためです。 シグナルは、新しいプロセスでBackgroundWorkerUnblockSignalsを呼び出すことにより解除でき、BackgroundWorkerBlockSignalsを呼び出すことでブロックできます。

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

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

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

登録時に得られたBackgroundWorkerHandle *を使用してWaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)関数を呼び出すことで、バックグラウンドワーカーがシャットダウンするのを待つこともできます。 バックグラウンドワーカーが終了するか、postmasterが死ぬまで、この関数はブロックします。 バックグラウンドワーカーが終了した場合の戻り値はBGWH_STOPPED、postmasterが死んだ場合の戻り値はBGWH_POSTMASTER_DIEDになります。

バックグラウンドワーカーは、SPI経由でNOTIFYコマンドを使用して、あるいはAsync_Notify()で直接、非同期通知メッセージを送ることができます。 そのような通知はトランザクションのコミット時に送信されます。 バックグラウンドワーカーは、LISTENコマンドによる非同期通知メッセージの受信登録をすべきではありません。 ワーカーがそのような通知を消費する基盤が存在しないからです。

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

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