bgworkerで超簡易クラスタ管理

本記事では、PostgreSQL9.3から新機能として入ったBackground Worker Processes(以下bgwoker)機能を使って __bgworkerモジュールを自作する際のポイント__ 、また、実際に自作してみた __超簡易クラスタ管理モジュール「pg_promoter」__ を紹介していきます。

# bgwokerとは？
PostgreSQLで別々のプロセスでユーザ提供のコードが実行されるように、拡張することができる機能です。
特徴としては、

- 内部的にデータベースに接続することが出来る
- libpqと連携することによって、クライアントアプリケーションとしてもデータベースに接続することが出来る
- bgworkerを起動するタイミングを選択することが出来る（例えば、サーバの初期化終了時や、スタンバイで参照クエリが受け付けられる状態になった時など）

などがあります。
詳しくは[公式ドキュメント](http://www.postgresql.jp/document/9.3/html/bgworker.html)をご参照ください。

# 自作bgworkerのポイント
bgworkerを自作するにあたり、実際に作成するときに感じたポイントを3つ紹介していきます。

##作りはじめる前に
PostgreSQLのcontribモジュールに __worker_spi__ というモジュールがあるので、ぜひそれを参考にすると作りやすいです。
最低限必要な機能が揃っているので、それに手を加えていくだけで、自作bgworkerが作れます。

##_PG_init()
bgworkerのロード時に呼ばれる_PG_init()関数についてです。

```c:_PG_init()
void
_PG_init(void)
{
    BackgroundWorker worker;
        
    /* 必要なパラメータの定義などをする */

    /* ここからはbgworkerについての設定 */
    worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
        BGWORKER_BACKEND_DATABASE_CONNECTION;
    worker.bgw_start_time = BgWorkerStart_ConsistentState;
    worker.bgw_restart_time = BGW_NEVER_RESTART;
    worker.bgw_main = pg_promoter_main;
    worker.bgw_notify_pid = 0;
    
    /* bgworkerの登録 */
    snprintf(worker.bgw_name, BGW_MAXLEN, "pg_promoter");
    RegisterBackgroundWorker(&worker);
}
```

ここでbgworker自体の設定をするのですが、その中でも`worker.bgw_flags`と`worker.start_time`についてを以下にまとめます。

- worker.bgw_flags (モジュールが要求する機能を設定)

|値|内容|
|:--|:--|
|BGWORKER_SHMEM_ACCESS|共有メモリへのアクセスを許可|
|BGWORKER_BACKEND_DATABASE_CONNECTION|トランザクションや、クエリの実行が可能|

- worker.start_time (bgworkerが起動するタイミングを設定)

|値|内容|
|:--|:--|
|BgWorkerStart_PostmasterStart |postgresの初期化が終わったら起動|
|BgWorkerStart_ConsistentState|参照のみのクエリを実行するのを許可したら起動|
|BgWorkerStart_RecoveryFinished|参照/更新クエリを実行すると起動|

##機能の作成
機能の作成はbgworkerに限らずですが、 __他のプログラムを参考にする__ とコードを書きやすいと思います。
次に紹介する「pg_promoter」では、pg_ctlやpgbench、postgres_fdwなどを参考にしながら作成しました。

# 超簡易クラスタ管理モジュール「pg_promoter」
bgworker機能を使って超簡易クラスタ管理モジュール「pg_promoter」というのを作ってみました。
「pg_promoter」は名前の通り、スタンバイサーバをただマスタにpromote(昇格)させるだけのシンプル(機能が足りないだけ？）なモジュールです。
主な特徴は以下の通りです。

- スタンバイサーバ側でのみ使用
- スタンバイサーバが参照クエリを受け付けられる状態になったタイミングで起動
- 設定項目は2項目(「マスタサーバへの接続情報」と「監視間隔」）
- 一定間隔でマスタサーバの死活監視を行う(スタンバイサーバ自身の死活監視は行わない)
- マスタサーバの死活監視はlibpqを使用してクライアントとして接続
- マスタサーバに異常があった場合は、スタンバイをマスタに昇格
- スタンバイサーバの昇格は内部でシグナルを送信

## pg_promoterの使い方
### pg_promoterのインストール
bgwokerとして登録するモジュールはcontribモジュールをインストールするときと同じように、`make`,`make install`でコンパイル、インストールします。

### postgresql.confへ登録
bgworkerを登録する場合は、「shared_preload_libraries」に設定します。

```html:postgresql.conf
shared_preload_libraries = 'pg_promoter'
# 以下は、pg_promoterに必要な設定項目です
pg_promoter.primary_conninfo = 'host=localhost port=15432'
pg_promoter.keep_alive = 5
```
上記の設定＋レプリケーションの設定をします。

### サーバの起動、bgwokerの起動
1. マスタサーバの起動

`pg_ctl start -D data`

2. スタンバイサーバの起動

`pg_ctl start -D 2data`

プロセスを見てみると、
> 14084 pts/2    S      0:00 /home/postgres/pgsql/9.3/bin/postgres -D data
> 14086 ?        Ss     0:00 postgres: checkpointer process
> 14087 ?        Ss     0:00 postgres: writer process
> 14088 ?        Ss     0:00 postgres: wal writer process
> 14089 ?        Ss     0:00 postgres: autovacuum launcher process
> 14090 ?        Ss     0:00 postgres: stats collector process
> 14105 pts/2    S      0:00 /home/postgres/pgsql/9.3/bin/postgres -D 2data
> 14109 ?        Ss     0:00 postgres: startup process   waiting for 000000010000000000000003
> 14110 ?        Ss     0:00 postgres: checkpointer process
> 14111 ?        Ss     0:00 postgres: writer process
> 14112 ?        Ss     0:00 postgres: stats collector process
> 14113 ?        Ss     0:00 postgres: bgworker: pg_promoter
> 14114 ?        Ss     0:00 postgres: wal receiver process   streaming 0/3000090
> 14115 ?        Ss     0:00 postgres: wal sender process postgres [local] streaming 0/3000090

となっており、`14113 ?        Ss     0:00 postgres: bgworker: pg_promoter`とbgworkerが起動していることがわかります。
つまり、PostgreSQLのバックグラウンドで「pg_promoter」がマスタ(PIDが14084)の死活監視を行っている状態となります。

### スタンバイサーバの昇格
この状態でスタンバイサーバのプロセスをkillしてみます。
```kill -SIGKILL 14084```

すると数秒後に。。
> LOG:  received promote request
> LOG:  redo done at 0/20000F0
> LOG:  selected new timeline ID: 2
> LOG:  worker process: pg_promoter (PID 14113) exited with exit code 1
> LOG:  unregistering background worker "pg_promoter"
> LOG:  archive recovery complete
> LOG:  database system is ready to accept connections
> LOG:  autovacuum launcher started

とログが出て、昇格しました！

#ソースコード
記事だと分かりにくいので、「ぜひ試していただきたい！」と思い、githubとかに置く予定だったのですが、なかなか時間がなくてあげられてない状態です。
近々githubに置くので、ぜひ使ってみてください！

(2013/12/24 追記)
gitubに公開しました！
[pg_promoter](https://github.com/Masahiko-Sawada/pg_promoter)
URL : https://github.com/Masahiko-Sawada/pg_promoter
ぜひ使ってみてください！

#まとめ
今回はbgworkerの機能を使って、簡易的なクラスタ管理モジュールを作ってみました。
最初は興味本位で作っていましたが、作り込めばまあまあ使えるクラスタ管理モジュールができるのではと感じました。
まだPostgreSQL9.3がリリースされてから4ヶ月程度ですが、色々なことが出来る機能ですので、これから様々なモジュールが出てくると思います。
bgworkerについてあまり情報が少ないので、bgworkerを自作する時や、他のbgworkerを使う際にご参考にしていただければと思います！
それでは良い週末を！