PostgreSQLはユーザ提供のコードを別のプロセスとして実行するように拡張できます。
このプロセスはpostgres
によって起動、終了、監視され、サーバの状態に密接にリンクした寿命を持つことができます。
これらのプロセスはPostgreSQLの共有メモリ領域にアタッチされ、データベースの内部に接続するオプションを持ちます。
これらはまた、通常のクライアントに接続された実際のサーバプロセスのように複数のトランザクションを連続して実行することができます。
また、アプリケーションはlibpqとリンクすることにより通常のクライアントアプリケーションのようにサーバに接続して動作することができます。
バックグラウンドワーカーを使うにあたっては、堅牢性とセキュリティリスクを考慮しなくてはなりません。なぜならば、C
言語で書かれており、データへのアクセスが制限されていないためです。
バックグラウンドワーカープロセスを含むモジュールを有効にしたいと思っている管理者は、細心の注意を払って実践してください。
バックグラウンドワーカープロセスの実行は、注意深く検査されたモジュールだけを許可する必要があります。
バックグラウンドワーカーは、モジュールをshared_preload_libraries
に記すことによって、PostgreSQLスタート時に初期化できます。
バックグラウンドワーカーとして実行したいモジュールは、_PG_init()
関数からRegisterBackgroundWorker(
を呼び出すことで登録できます。
バックグラウンドワーカーはシステム起動後もBackgroundWorker
*worker
)RegisterDynamicBackgroundWorker(
を呼び出すことによって開始することができます。
postmasterプロセスからのみ呼び出すことができるBackgroundWorker
*worker
, BackgroundWorkerHandle
**handle
)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_name
やbgw_type
は、ログメッセージ、プロセス一覧、および同様の場面で使用される文字列です。
bgw_type
は、同じ種類のバックグラウンドワーカーで全て同じになるため、例えば同じ種類のワーカーをプロセス一覧でグループ化することができます。
一方でbgw_name
は、特定のプロセスに関する追加情報を含むことができます。
(通常、bgw_name
の文字列は何らかの形で種類に関する情報を含んでいますが、必須であるというわけではありません。)
bgw_flags
は、モジュールが要求する機能をOR演算したビットマスクです。可能な値は以下の通りです。
bgw_start_time
は、postgres
がプロセスを起動するべきタイミングを指定します。
そのタイミングは、以下のうちの1つです。
BgWorkerStart_PostmasterStart
(postgres
自身が初期化を終えるとすぐに起動します。これを要求するプロセスはデータベース接続に望ましいものではありません)、
BgWorkerStart_ConsistentState
(ホットスタンバイで一貫性のある状態に到達し、データベースに接続して参照のみのクエリが実行できるようになると起動します)、
BgWorkerStart_RecoveryFinished
(システムが通常の参照/更新クエリを実行できるようになると起動します)。
最後の2つの値は、ホットスタンバイでないサーバでは同等であることに注意してください。
この設定はいつプロセスが起動されるかを示すだけであることに注意してください。
これらのプロセスは、違う状態になったときに停止するわけではありません。
bgw_restart_time
は、万が一プロセスがクラッシュした場合にpostgres
がそのプロセスを再起動するために待つ必要のある間隔を秒単位で指定します。
これは任意の正の値、またはクラッシュしても再起動させない場合にBGW_NEVER_RESTART
を指定します。
bgw_library_name
は、バックグラウンドワーカーの初期エントリポイントのためのライブラリ名です。
その指定されたライブラリがワーカープロセスによって動的にロードされます。呼び出すべき関数を特定するためにbgw_function_name
が使用されます。
コアコード内の関数を呼び出す場合、"postgres"
を設定する必要があります。
bgw_function_name
は、新しいバックグラウンドワーカーの初期エントリポイントとして使用する関数の名前です。
この関数が動的にロードされたライブラリ内にある場合、PGDLLEXPORT
(static
ではない)とマークする必要があります。
bgw_main_arg
は、バックグラウンドワーカーのメイン関数のDatum
引数です。
メイン関数は単一のDatum
引数を取り、void
を返します。
bgw_main_arg
は引数として渡されます。
加えて、グローバル変数MyBgworkerEntry
は、登録時に渡されたBackgroundWorker
構造体のコピーを指しています。
ワーカーはこの構造を調べることがあり、役に立ちます。
Windowsの(どこか他の場所でEXEC_BACKEND
が定義されている)場合、または動的バックグラウンドワーカーは、Datum
を参照で渡すのは安全ではありません。値のみで渡してください。
引数が必要な場合は、int32型または他の小さな値を渡し、共有メモリに割り当てられた配列へのインデックスとしてそれを使用するのが最も安全です。
cstring
やtext
のようなポインタを渡された場合は、新しいバックグラウンドワーカープロセスから有効になりません。
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であった場合、またはdboid
がInvalidOid
であった場合には、そのセッションは特定のデータベースに接続しません。しかし、共有カタログにはアクセス出来ます。
もし、username
がNULLの場合、またはuseroid
がInvalidOid
の場合には、そのプロセスはinitdb
時に作成されたスーパーユーザとして実行されます。
flags
としてBGWORKER_BYPASS_ALLOWCONN
が設定されている場合、データベースへ接続する際のユーザ接続を許可しない制約を回避することが出来ます。
バックグラウンドワーカーはこれら2つの関数をどちらかを一度だけ呼ぶことが出来ます。
データベースを切り替えることができません。
バックグラウンドワーカーのメイン関数に制御が達したとき、シグナルは最初にブロックされています。このブロックは解除されなければなりません。
これは、必要に応じてプロセスがシグナルハンドラをカスタマイズできるようにするためです。
シグナルは、新しいプロセスでBackgroundWorkerUnblockSignals
を呼び出すことにより解除でき、BackgroundWorkerBlockSignals
を呼び出すことでブロックできます。
バックグラウンドワーカーは、bgw_restart_time
がBGW_NEVER_RESTART
に設定されている場合、または終了コード0で終了した場合、またはTerminateBackgroundWorker
によって終了した場合、postmasterに自動的に登録が解除されて終了します。
それ以外の場合、bgw_restart_time
で設定された時間の後に再起動します。または、バックエンドの障害のためにpostmasterがクラスタを再初期化した場合は、すぐに再起動します。
一時的に実行を中断するだけでよいバックエンドは、終了するのではなく、割り込み可能なスリープを使用する必要があります。
これはWaitLatch()
を呼び出すことによって可能になります。
この関数を呼び出すときにはWL_POSTMASTER_DEATH
フラグが設定されているか確認し、postgres
自身が終了する緊急事態には、リターンコードを確認するようにしてください。
バックグラウンドワーカーをRegisterDynamicBackgroundWorker
関数により登録している場合、登録を実行するバックエンドはワーカーの状態に関する情報を取得することが可能です。
取得したい場合はRegisterDynamicBackgroundWorker
に2番目の引数としてBackgroundWorkerHandle *
のアドレスを渡す必要があります。
もし登録に成功した場合、このポインタは後でGetBackgroundWorkerPid(
またはBackgroundWorkerHandle *
,pid_t *
)TerminateBackgroundWorker(
に渡すことができるopaque(不透明)ハンドルで、初期化されます。
BackgroundWorkerHandle *
)GetBackgroundWorkerPid
はワーカーの状態を監視することができます。以下の返り値が得られます。
BGWH_NOT_YET_STARTED
ワーカーはまだpostmasterにより開始されていない。
BGWH_STOPPED
開始されたが、もはや実行されていない。
BGWH_STARTED
実行中です。
この最後のケースでは、PIDは、2番目の引数を介して返されます。
TerminateBackgroundWorker
はワーカーが実行していた場合postmasterがワーカーにSIGTERM
を送信し、実行が終了次第すぐに登録を解除します。
場合によっては、バックグラウンドワーカーが起動するのを待ってから、ワーカーを登録したい場合もあるでしょう。
これは bgw_notify_pid
をMyProcPid
で初期化し、登録時に得られたBackgroundWorkerHandle *
を使用してWaitForBackgroundWorkerStartup(
関数を呼び出すことで実現します。
postmasterがバックグラウンドワーカーを開始しようと試みたか、postmasterが死ぬまで、この関数はブロックします。
バックグラウンドワーカーが実行されている場合、戻り値はBackgroundWorkerHandle *handle
,pid_t *
)BGWH_STARTED
となり、指定されたアドレスにPIDが書き込まれます。
そうでない場合、戻り値はBGWH_STOPPED
またはBGWH_POSTMASTER_DIED
になります。
登録時に得られたBackgroundWorkerHandle *
を使用してWaitForBackgroundWorkerShutdown(
関数を呼び出すことで、バックグラウンドワーカーがシャットダウンするのを待つこともできます。
バックグラウンドワーカーが終了するか、postmasterが死ぬまで、この関数はブロックします。
バックグラウンドワーカーが終了した場合の戻り値はBackgroundWorkerHandle *handle
)BGWH_STOPPED
、postmasterが死んだ場合の戻り値はBGWH_POSTMASTER_DIED
になります。
バックグラウンドワーカーは、SPI経由でNOTIFY
コマンドを使用して、あるいはAsync_Notify()
で直接、非同期通知メッセージを送ることができます。
そのような通知はトランザクションのコミット時に送信されます。
バックグラウンドワーカーは、LISTEN
コマンドによる非同期通知メッセージの受信登録をすべきではありません。
ワーカーがそのような通知を消費する基盤が存在しないからです。
バックグラウンドワーカーの実例として、src/test/modules/worker_spi
というモジュールがあります。
これはいくつかの有用な技術を示しています。
登録できるバックグラウンドワーカーの最大数はmax_worker_processesによって制限されています。