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]; int bgw_notify_pid; } BackgroundWorker;
bgw_name
やbgw_type
は、ログメッセージ、プロセス一覧、および同様の場面で使用される文字列です。
bgw_type
は、同じ種類のバックグラウンドワーカで全て同じになるため、例えば同じ種類のワーカをプロセス一覧でグループ化することができます。
一方でbgw_name
は、特定のプロセスに関する追加情報を含むことができます。
(通常、bgw_name
の文字列は何らかの形で種類に関する情報を含んでいますが、必須であるというわけではありません。)
bgw_flags
は、モジュールが要求する機能をOR演算したビットマスクです。可能な値は以下の通りです。
BGWORKER_SHMEM_ACCESS
共有メモリへのアクセスを要求します。 共有メモリアクセスがないワーカは、重量または軽量のロック、共有バッファ、ワーカが作成して利用したいカスタムデータ構造等、PostgreSQLの共有データ構造にアクセスできません。
BGWORKER_BACKEND_DATABASE_CONNECTION
トランザクションやクエリの実行が出来るデータベース接続を要求します。
BGWORKER_BACKEND_DATABASE_CONNECTION
を使用してデータベースに接続するバックグラウンドワーカはBGWORKER_SHMEM_ACCESS
を使用して共有メモリにアタッチしなければなりません。さもなければ起動時に失敗します。
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
は新しいバックグラウンドワーカから動的にロードされるときに初期エントリポイントの関数名です。
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
コマンドにより非同期に通知を送る場合、囲んでいるトランザクションをコミットした後、通知を配信することができるように明示的にProcessCompletedNotifies
を呼ぶ必要があります。
バックグラウンドワーカは、SPIを通じてLISTEN
による非同期通知の受信を登録した場合、ワーカがこれらの通知をログに記録しますが、ワーカが傍受し、それらの通知に応答するためのプログラム的な方法はありません。
バックグラウンドワーカの実例として、src/test/modules/worker_spi
というモジュールがあります。
これはいくつかの有用な技術を示しています。
登録できるバックグラウンドワーカの最大数はmax_worker_processesによって制限されています。