#author("2018-01-13T16:34:48+00:00","default:haikikyou","haikikyou")
[[PostgreSQL/開発]]
#contents
* バックグラウンドワーカプロセス [#q64b878d]
PostgreSQLでは、クライアントコネクション接続を処理するバックエンドプロセスとは異なるプロセスを実行することができる。このバックグラウンドワーカプロセスは、''postmasterプロセスによって起動''(fork)され、ワーカプロセスの状態はpostmasterの起動・停止・監視とリンクしている。このバックグラウンドワーカの仕組みは、パラレルワーカでも使われており、バックエンドプロセスから動的にバックグラウンドワーカプロセスを登録(※)することも可能である。
バックグラウンドワーカは、バージョン9.3から機能追加されている。~
9.4からは動的にバックグラウンドワーカを起動できるように拡張されている。
#ref(./background-worker-overview.png,100%)
&label(info){補足};※ 登録と記述しているのは、実際にプロセス生成を行なうのがpostmasterプロセスであるためである。バックグラウンドワーカの起動は、呼び出し側がposmasterにシグナル送信(SIGUSR1)し、postmasterがforkすることで実現されている。
* バックグラウンドワーカプロセスのフロー [#j230d805]
バックグラウンドワーカは、Postmaster起動時及び起動後のタイミングで登録することができる(便宜上、ここではPostmaster起動時のワーカを静的、Posmaster起動後のワーカを動的と呼ぶこととする)。静的、動的では以下の違いがある。
- 静的バックグラウンドワーカ('''BackgroundWorker''')
-- ''%%%postmaster起動時%%%''(postmasterプロセスからの呼び出し)
-- shared_preload_librariesの初期化中
- 動的バックグラウンドワーカ('''DynamicBackgroundWorker''')
--''%%% postmaster起動時以外%%%''(''NOT'' postmasterプロセスからの呼び出し)。バックエンドプロセス、静的バックグラウンドワーカ、動的バックグラウンドワーカから登録が可能。
静的・動的でバックグラウンドワーカを登録する関数は異なるが、バックグラウンドワーカ自体は、同じ構造体変数で表現され、共有メモリ上のバックグラウンドワーカ配列で管理される。
#ref(./background-worker-process-image.png,100%)
* BackgroundWorker構造体 [#wdbe9fdd]
|~番号|~データ型|~フィールド|~説明|h
|1|char|bgw_name[BGW_MAXLEN]|バックグラウンドワーカの名前。プロセスリスト、ログで使用される。|
|2|int|bgw_flags|バックグラウンドワーカが要求する機能。&br;●&code(){BGWORKER_SHMEM_ACCESS};&br;共有メモリへのアクセスをする。&br;●&code(){BGWORKER_BACKEND_DATABASE_CONNECTION};&br;データベース接続をする。&label(fatal){注意};データベース接続をする場合は、&code(){BGWORKER_SHMEM_ACCESS};も指定する必要がある。|
|3|BgWorkerStartTime|bgw_start_time|バックグラウンドワーカの起動タイミング。&label(warn){参考};[[bgworker_should_start_now()>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/postmaster/postmaster.c;h=a8e19c92381dc0de643b1837149ff148026ce175;hb=67854bc59a8b283f02f2a8f0c0095df639bedf06#l5749]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/};};&br;●&code(){BgWorkerStart_PostmasterStart};&br;postmasterの起動時でServerLoop()の前(pmState = &code(){PM_STARTUP};、StartupStatus = &code(){STARTUP_RUNNING};)。&br;リカバリ開始のタイミング &label(warn){参考};[[Postmasterプロセス>PostgreSQL/解析/Postmasterプロセス]]&br;●&code(){BgWorkerStart_ConsistentState};&br;ホットスタンバイモードで参照クエリの受け付けが可能になったタイミング(pmState = &code(){PM_HOT_STANDBY};、次のログが確認されたタイミング:&code(){database system is ready to accept read only connections};)&br;●&code(){BgWorkerStart_RecoveryFinished};&br;リカバリが完了し、システムが通常の参照/更新クエリを実行できるようになった時(pmState = &code(){PM_RUN};、StartupStatus = &code(){STARTUP_NOT_RUNNING};)&br;&label(warn){参考};[[reaper()>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/postmaster/postmaster.c;h=a8e19c92381dc0de643b1837149ff148026ce175;hb=67854bc59a8b283f02f2a8f0c0095df639bedf06#l2900]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/};};|
|4|int|bgw_restart_time|バックグラウンドワーカがクラッシュ(終了コード0以外)してから再起動するまでの時間間隔(秒)。&br;&code(){BGW_NEVER_RESTART};が指定された場合は再起動しない。|
|5|char|bgw_library_name[BGW_MAXLEN]|バックグラウンドワーカモジュールのライブラリ名。ワーカのコードを含む拡張機能モジュールのファイル名。モジュールの動的ロードに必要。|
|6|char|bgw_function_name[BGW_MAXLEN]|バックグラウンドワーカのエントリーポイント。&code(){entrypoint(Datum main_arg)};という呼び出しとなる。|
|7|Datum|bgw_main_arg|バックグラウンドワーカのエントリーポイントに渡される引数。|
|8|char|bgw_extra[BGW_EXTRALEN]|引数以外にデータの受け渡しが必要な場合に使用可能な領域。|
|9|pid_t|bgw_notify_pid|バックグラウンドワーカの終了を通知するプロセスID(&code(){SIGUSR1};シグナルが送信される)。通常は、呼び出したプロセスIDで&code(){MyProcPid};を指定する。|
バックグラウンドワーカの登録は、&code(){BackgroundWorker};構造体を介して行なう。以下、静的バックグラウンドワーカを登録する例である。
&label(sample){サンプル}; ''静的バックグラウンドワーカの登録''
&code(){src/test/modules/worker_spi};にあるサンプルが大変参考になる。
#geshi(c){{{
void
_PG_init(void)
{
BackgroundWorker worker;
memset(&worker, 0, sizeof(worker));
// ワーカは、データベース接続を必要とする
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
BGWORKER_BACKEND_DATABASE_CONNECTION;
/* リカバリ完了しクエリの受け付け可能になったタイミングで起動 */
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
/* クラッシュしても再起動しない */
worker.bgw_restart_time = BGW_NEVER_RESTART;
/* モジュール名 */
sprintf(worker.bgw_library_name, "myworker");
/* エントリーポイント */
sprintf(worker.bgw_function_name, "myworker_main");
/* 静的バックグラウンドワーカの場合は、0以外は無効(ワーカが登録されない) */
worker.bgw_notify_pid = 0;
/* プロセスリストやログに表示される名前 */
snprintf(worker.bgw_name, BGW_MAXLEN, "myworker");
/* ワーカスロットに登録 */
RegisterBackgroundWorker(&worker);
}
/*
* ワーカのエントリポイント
*/
void myworker_main(Datum main_arg)
{
/* ワーカの処理 */
}
}}}
* バックグラウンドワーカの終了 [#sf4c89d1]
** 静的バックグラウンドワーカ [#e2850928]
postmasterの停止でワーカも停止する。~
ワーカのみ停止したい場合は、シグナルを送ることで可能(ブロックや停止拒否していない場合)。
** 動的バックグラウンドワーカ [#ydbaae1c]
バックグラウンドワーカ(ワーカ)を登録するプロセスを便宜上オーナと呼ぶ。
** オーナが終了 [#r74a682f]
ワーカはオーナの終了を知ることはない。&code(){BackgroundWorker}; 構造体のメンバ変数&code(){bgw_notify_pid};に&code(){0};以外を指定していた場合、オーナが終了すると&code(){BackgroundWorkerStopNotifications};関数が呼ばれ、&code(){bgw_notify_pid};が&code(){0};に設定される。従って、ワーカでこの変数をチェックすればオーナの終了を知ることができる。
また、別の方法として&code(){on_proc_exit};による終了ハンドラを利用するという方法も考えられる。通常、ワーカはループ内でLatchによるWaitを使って何らかの処理を繰り返し実行するような実装になると思うが、オーナありきのワーカの場合、オーナの終了をできるだけ早期に検知し自身も停止した方が、ワーカスロットの無駄な占有もしなくて済むはず。
** ワーカの終了を待つ [#l4d027a5]
バッググラウンドワーカの
** ワーカの終了通知(SIGUSR1)を受けとる [#c11f64be]
バックグラウンドワーカの登録時、ワーカを登録するプロセス(ここでは、オーナと呼ぶ)は、BackgroundWorker構造体変数の&code(){bgw_notify_pid};に自身のプロセスIDを指定すると、ワーカ終了の通知をシグナル&code(){SIGUSR1};で受け取ることができる。
&label(sample){サンプル}; ''オーナでワーカの終了通知を受け取る''
#geshi(c){{{
static volatile sig_atomic_t got_sigusr1 = false;
static volatile sig_atomic_t got_sigterm = false;
static void
myworker_sigusr1(SIGNAL_ARGS)
{
int save_errno = errno;
got_sigusr1 = true;
SetLatch(MyLatch);
errno = save_errno;
}
static void
myworker_sigterm(SIGNAL_ARGS)
{
int save_errno = errno;
got_sigterm = true;
SetLatch(MyLatch);
errno = save_errno;
}
void myworker_main(Datum main_arg)
{
pqsignal(SIGUSR1, myworker_sigusr1);
pqsignal(SIGTERM, myworker_sigterm);
/* Now we can receive signals */
BackgroundWorkerUnblockSignals();
while (!git_sigterm)
{
if (got_sigusr1)
{
got_sigusr1 = false;
/* Do something */
}
/*
* Do something
*/
}
proc_exit(1);
}
}}}
* 参考リンク [#o7ea3c3f]
- [[並列クエリを実行するPostgreSQLのアーキテクチャ>https://www.slideshare.net/kaigai/postgresql-64441503]] - &size(11){&color(gray){https://www.slideshare.net/kaigai/};};
- [[PostgreSQL 9.6.X 文書 第45章 バックグラウンドワーカプロセス>https://www.postgresql.jp/document/9.6/html/bgworker.html]] - &size(11){&color(gray){https://www.postgresql.jp/document/9.6/html/};};
- [[Chapter 47. Background Worker Processes>https://www.postgresql.org/docs/10/static/bgworker.html]] - &size(11){&color(gray){https://www.postgresql.org/docs/10/static/};};
- [[postmaster/bgworker.c>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/postmaster/bgworker.c;h=b690b1b0b85e905572919f86255c0f6c09f54b16;hb=b8279a783dafb7c715b3835d4c9244d3451c4c1a]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/?p=postgresql.git;a=shortlog;h=refs/heads/REL_10_STABLE};};
- [[PostgreSQL のバックグラウンドワーカー(Background Worker)の使い方>http://www.nminoru.jp/~nminoru/postgresql/pg-background-worker.html#struct-background-worker]] - &size(11){&color(gray){http://www.nminoru.jp/~nminoru/postgresql/};};