Pgpool-II 4.2 で何がどう変わったか？

この記事は [PostgreSQL Advent Calendar 2020](https://qiita.com/advent-calendar/2020/postgresql) の 21 日目です。

この記事では、Pgpool-II 4.2 の変更点について新機能を中心に紹介します。[Pgpool-II](https://www.pgpool.net/jp/) は PostgreSQL と組み合わせてフェイルオーバや負荷分散などの機能を提供するミドルウェアです。[バージョン 4.2](https://www.pgpool.net/docs/latest/ja/html/release-4-2-0.html) は 2020 年 11 月にリリースされた、およそ 1 年ぶりのメジャーバージョンになります。

それでは、早速、Pgpool-II 4.2 の変更点を見ていきましょう。

# 動作モード設定の集約

Pgpool-II にはさまざまな動作モードがあります。動作モードはバージョンを重ねるたびに増えてきたこともあり、バージョン 4.1 以前では、`replication_mode`、`master_slave_mode`、`master_slave_sub_mode` の 3 つのパラメータで設定していました。バージョン 4.2 では、これらがクラスタリングモードとして  1 つのパラメータ `backend_clustering_mode` に集約され、設定がシンプルになりました。([マニュアル](https://www.pgpool.net/docs/latest/ja/html/runtime-config-running-mode.html))

バージョン 4.1 以前と 4.2 とのパラメータの違いは以下のとおりです。

![clustering-mode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160683/22840596-ffef-3098-53be-f0870d8dec3e.png)

例えば、ストリーミングレプリケーションモードを使う場合、バージョン 4.1 以前では以下の設定になります。

```config:バージョン&nbsp;4.1&nbsp;以前
replication_mode = off
master_slave_mode = on
master_slave_sub_mode = 'stream'
```

バージョン 4.2 では以下の設定になります。

```config:バージョン&nbsp;4.2
backend_clustering_mode = 'streaming_replication'
```

ちなみに、スナップショットアイソレーションモードはバージョン 4.2 で追加されたクラスタリングモードで、あとで少しだけ紹介します。

# Watchdog 設定のノード間共通化

Watchdog は複数の Pgpool-II を互いに監視して冗長化する機能です。バージョン 4.1 以前では、以下のように、Watchdog の設定は自ノードと他ノードで分かれており、ノードごとに異なる設定を行っていました。

![watchdog-41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160683/1dd8f435-0424-1df1-0a33-5306a7ef1c0d.png)

バージョン 4.2 では、以下のように、Watchdog の設定はノード間で共通化され、全ノードで同じ設定を行い、自ノードの設定かどうかは `pgpool_node_id` ファイルで識別するようになりました。これにより、ノードごとに設定を変える手間が省けます。([マニュアル](https://www.pgpool.net/docs/latest/ja/html/runtime-watchdog-config.html))

![watchdog-42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160683/75cd2b20-7485-9c58-f1ac-77a2616c4f2f.png)

バージョン 4.1 以前と 4.2 とのパラメータの違いは以下のとおりです。

![watchdog.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160683/e98451ec-ed96-8a5a-1937-b48afc70e7da.png)

パラメータ名の最後の `#` には、バージョン 4.1 以前ではノードごとに 0 から始まる番号、バージョン 4.2 ではノード ID が入ります。

ハートビートは Watchdog による死活監視の 1 つで、1 つのノードに対して複数の送信先を設定できます。バージョン 4.1 以前では番号を増やしてパラメータを追加し、1 つのパラメータに 1 つの設定を行っていましたが、バージョン 4.2 では 1 つのパラメータに複数の設定を `;` 区切りで設定するようになりました。

例えば、ノードが `node-0`、`node-1`、`node-2` の 3 台で、ハートビートを冗長化している場合、バージョン 4.1 以前では `node-0` の設定は以下になります。

```config:バージョン&nbsp;4.1&nbsp;以前&nbsp;(node-0&nbsp;の場合)
# 自ノードの設定
wd_hostname = 'node-0'
wd_port = 9000
wd_heartbeat_port = 9694

# 他ノードの設定
other_pgpool_hostname0 = 'node-1'
other_pgpool_port0 = 9999
other_wd_port0 = 9000
heartbeat_destination0 = 'node-1'
heartbeat_destination_port0 = 9694
heartbeat_device0 = 'eth0'
heartbeat_destination1 = 'node-1-alt'
heartbeat_destination_port1 = 9694
heartbeat_device1 = 'eth1'

other_pgpool_hostname1 = 'node-2'
other_pgpool_port1 = 9999
other_wd_port1 = 9000
heartbeat_destination2 = 'node-2'
heartbeat_destination_port2 = 9694
heartbeat_device2 = 'eth0'
heartbeat_destination3 = 'node-2-alt'
heartbeat_destination_port3 = 9694
heartbeat_device3 = 'eth1'
```

`node-1`、`node-2` の設定を確認するには、以下をクリックしてください。

<details>
<summary>`node-1`、`node-2` の設定</summary>
<div>

```config:バージョン&nbsp;4.1&nbsp;以前&nbsp;(node-1&nbsp;の場合)
# 自ノードの設定
wd_hostname = 'node-1'
wd_port = 9000
wd_heartbeat_port = 9694

# 他ノードの設定
other_pgpool_hostname0 = 'node-0'
other_pgpool_port0 = 9999
other_wd_port0 = 9000
heartbeat_destination0 = 'node-0'
heartbeat_destination_port0 = 9694
heartbeat_device0 = 'eth0'
heartbeat_destination1 = 'node-0-alt'
heartbeat_destination_port1 = 9694
heartbeat_device1 = 'eth1'

other_pgpool_hostname1 = 'node-2'
other_pgpool_port1 = 9999
other_wd_port1 = 9000
heartbeat_destination2 = 'node-2'
heartbeat_destination_port2 = 9694
heartbeat_device2 = 'eth0'
heartbeat_destination3 = 'node-2-alt'
heartbeat_destination_port3 = 9694
heartbeat_device3 = 'eth1'
```

```config:バージョン&nbsp;4.1&nbsp;以前&nbsp;(node-2&nbsp;の場合)
# 自ノードの設定
wd_hostname = 'node-2'
wd_port = 9000
wd_heartbeat_port = 9694

# 他ノードの設定
other_pgpool_hostname0 = 'node-0'
other_pgpool_port0 = 9999
other_wd_port0 = 9000
heartbeat_destination0 = 'node-0'
heartbeat_destination_port0 = 9694
heartbeat_device0 = 'eth0'
heartbeat_destination1 = 'node-0-alt'
heartbeat_destination_port1 = 9694
heartbeat_device1 = 'eth1'

other_pgpool_hostname1 = 'node-1'
other_pgpool_port1 = 9999
other_wd_port1 = 9000
heartbeat_destination2 = 'node-1'
heartbeat_destination_port2 = 9694
heartbeat_device2 = 'eth0'
heartbeat_destination3 = 'node-1-alt'
heartbeat_destination_port3 = 9694
heartbeat_device3 = 'eth1'
```
</div>
</details>

バージョン 4.2 では全ノード共通で以下の設定になります。

```config:バージョン&nbsp;4.2
# 全ノードの設定
hostname0 = 'node-0'
pgpool_port0 = 9999
wd_port0 = 9000
heartbeat_hostname0 = 'node-0;node-0-alt'
heartbeat_port0 = 9694
heartbeat_device0 = 'eth0;eth1'

hostname1 = 'node-1'
pgpool_port1 = 9999
wd_port1 = 9000
heartbeat_hostname1 = 'node-1;node-1-alt'
heartbeat_port1 = 9694
heartbeat_device1 = 'eth0;eth1'

hostname2 = 'node-2'
pgpool_port2 = 9999
wd_port2 = 9000
heartbeat_hostname2 = 'node-2;node-2-alt'
heartbeat_port2 = 9694
heartbeat_device = 'eth0;eth1'
```

さらにバージョン 4.2 では設定ファイル `pgpool.conf` と同じディレクトリ内に `pgpool_node_id` というファイルを作成し、ノードごとにパラメータ名の最後につけたノード ID、例えば、`node-0` の場合には `0` を設定しておく必要があります。

# ログ取得コレクタ

ログ取得コレクタ (logging collector) は標準エラーに出力されたログを収集し、ファイルに保存するバックグラウンドプロセスです。PostgreSQL には以前からあった機能ですが、バージョン 4.2 から Pgpool-II でも使えるようになりました。([マニュアル](https://www.pgpool.net/docs/latest/ja/html/runtime-config-logging.html#RUNTIME-CONFIG-LOGGING-WHERE-TO-LOG))

パラメータは PostgreSQL と同じで、起動するには `logging_collector` パラメータを `on` に設定します。

```config
log_destination = 'stderr'
logging_collector = on
log_directory = '/tmp/pgpool_log'
log_filename = 'pgpool-%Y-%m-%d_%H%M%S.log'
log_file_mode = 0600
log_truncate_on_rotation = off
log_rotation_age = 1d
log_rotation_size = 10MB
```

ログファイルの保存やローテーションは、バージョン 4.1 以前でも syslog を使うか、標準エラーをパイプで rotatelogs などに渡せば、できていましたが、バージョン 4.2 では Pgpool-II のみで行えるようになりました。

# `pg_enc`/`pg_md5` コマンドによる一括登録

`pg_enc`/`pg_md5` コマンドはクライアント認証の `pool_passwd` ファイルや設定ファイル `pgpool.conf` の `*_password` パラメータに設定するパスワードを AES256/MD5 で暗号化するコマンドです。(マニュアル [pg_enc](https://www.pgpool.net/docs/latest/ja/html/pg-enc.html)、[pg_md5](https://www.pgpool.net/docs/latest/ja/html/pg-md5.html))

バージョン 4.2 では `-i`/`--input-file` オプションが追加され、1 行につき 1 つ、ユーザ名とパスワードを `:` 区切りで記述したファイルを指定し、まとめて一括で登録できるようになりました。

例えば、ファイル `users.txt` からユーザ情報を読み込み、パスワードを AES256 で暗号化し、`pool_passwd` ファイルに登録するには、以下のようになります。

```shell-session
$ cat /etc/pgpool-II/pool_passwd
postgres:AESbPro4vs9cT622am0+RjU3Q==
$ cat users.txt
user1:password1
user2:password2
$ pg_enc -m -f /etc/pgpool-II/pgpool.conf -i users.txt
trying to read key from file /var/lib/pgsql/.pgpoolkey
trying to read username:password pairs from file users.txt
$ cat /etc/pgpool-II/pool_passwd
postgres:AESbPro4vs9cT622am0+RjU3Q==
user1:AESP3i/0t42TFJCJJ/PsOaLfQ==
user2:AES7DluC6SlSGW7rf7pLqJWAg==
```

# ヘルスチェック/バックエンド統計情報の表示

Pgpool-II には Pgpool-II に接続して SQL として実行する SQL 型コマンドがあります。バージョン 4.2 では SQL 型コマンドにヘルスチェック/バックエンドの統計情報を表示する `SHOW POOL_HEALTH_CHECK_STATS` / `SHOW POOL_BACKEND_STATS` が追加されました。(マニュアル [SHOW POOL_HEALTH_CHECK_STATS](https://www.pgpool.net/docs/latest/ja/html/sql-show-pool-health-check-stats.html)、[SHOW POOL_BACKEND_STATS](https://www.pgpool.net/docs/latest/ja/html/sql-show-pool-backend-stats.html))

Pgpool-II では Pgpool-II が管理する PostgreSQL サーバのことをバックエンドと呼び、バックエンドが正常に動作しているかのチェックをヘルスチェックと呼びます。

ヘルスチェックの統計情報では、バックエンドごとにヘルスチェックが何回実行されたか (`total_count`)、何回成功したか (`success_count`)、最後にいつ失敗したか (`last_failed_health_check`)、などの情報を確認できます。

```shell-session
$ psql -x -p 9999 -c "SHOW POOL_HEALTH_CHECK_STATS"
-[ RECORD 1 ]----------------+--------------------
node_id                      | 0
hostname                     | node-0
port                         | 5432
status                       | up
role                         | primary
last_status_change           | 2020-12-17 07:53:14
total_count                  | 224
success_count                | 224
fail_count                   | 0
skip_count                   | 0
retry_count                  | 0
average_retry_count          | 0.000000
max_retry_count              | 0
max_duration                 | 288
min_duration                 | 8
average_duration             | 40.308036
last_health_check            | 2020-12-17 08:24:48
last_successful_health_check | 2020-12-17 08:24:48
last_skip_health_check       |
last_failed_health_check     |
(省略)
```

バックエンドの統計情報では、バックエンドごとに `SELECT`、`INSERT`、`UPDATE`、`DELETE` が何回実行されたか (`select_cnt`、`insert_cnt`、`update_cnt`、`delete_cnt`)、エラーレベルごとにエラーが何回発生したか (`panic_cnt`、`fatal_cnt`、`error_cnt`)、などの情報を確認できます。

```shell-session
$ psql -x -p 9999 -c "show pool_backend_stats"
-[ RECORD 1 ]-------
node_id    | 0
hostname   | node-1
port       | 5432
status     | up
role       | primary
select_cnt | 28228
insert_cnt | 28335
update_cnt | 84675
delete_cnt | 0
ddl_cnt    | 12
other_cnt  | 56489
panic_cnt  | 0
fatal_cnt  | 0
error_cnt  | 0
(省略)
```

# PCP コマンドによる設定ファイル再読み込み

PCP コマンドは PCP (Pgpool Control Protocol) を使って Pgpool-II を制御するコマンドです。バージョン 4.2 では PCP コマンドに設定ファイルの再読み込みを行う `pcp_reload_config` コマンドが追加されました。([マニュアル](https://www.pgpool.net/docs/latest/ja/html/pcp-reload-config.html))

設定ファイルの再読み込みは、バージョン 4.1 以前でも　`pgpool reload` コマンドで行えていましたが、PCP コマンドとして追加されたことにより、リモートからでも行えるようになりました。また、`-s`/`--scope=c` (`cluster`) オプションで全ノードでまとめて再読み込みを行うこともできます。

```shell-session
$ pcp_reload_config -w -s c
pcp_reload_config -- Command Successful
```

# 読み取り専用/書き込み関数の自動設定

Pgpool-II では raw モード以外であれば読み取りクエリを負荷分散できます。読み取りクエリかどうかは SQL を見れば分かりますが、関数で書き込みを行っていると判断がつきません。

そのため、`read_only_function_list` (バージョン 4.1 以前では `white_function_list`) パラメータに読み取り専用関数を設定し、それのみを負荷分散するか、逆に `write_function_list` (バージョン 4.1 以前では `black_function_list`) パラメータに書き込み関数を設定し、それ以外を負荷分散するかの設定を行う必要があります。([マニュアル](https://www.pgpool.net/docs/latest/ja/html/runtime-config-load-balancing.html))

バージョン 4.1 以前ではデフォルトで `black_function_list` パラメータのみにシーケンス操作関数が設定されていました。

```config:バージョン&nbsp;4.1&nbsp;以前
white_function_list = ''
black_function_list = 'currval,lastval,nextval,setval'
```

バージョン 4.2 ではデフォルトで `read_only_function_list`/`write_function_list` パラメータは両方とも空に設定されています。その場合、最初に関数の変動性 (volatility) をチェックし、変更を伴うことを示す `VOLATILE` 以外であれば、負荷分散するようになっています。いずれかのパラメータを空以外に設定した場合には、バージョン 4.1 以前と同じ動作になります。

```config:バージョン&nbsp;4.2
read_only_function_list = ''
write_function_list = ''
```

# スナップショットアイソレーションモード

スナップショットアイソレーションモードはバージョン 4.2 で追加されたクラスタリングモードです。([マニュアル](https://www.pgpool.net/docs/42/ja/html/runtime-config-running-mode.html#GUC-SNAPSHOT-ISOLATION-MODE))

書き込みクエリを全バックエンドで実行してレプリケーションを実現している点はネイティブレプリケーションモードと同じですが、ノード間の読み取り一貫性が保証されている点が異なります。

スナップショットアイソレーションモードについては @harukat1232000 さんが「[Pgpool-II スナップショットアイソレーションモードの話 (1)](https://qiita.com/harukat1232000/items/a8f1aa5f911528aae332)」で詳しく紹介していくそうなので、そちらを読んでいただくのがよいでしょう。

# 中立的な用語への変更

バージョン 4.2 ではパラメータ名やコマンド出力、マニュアルで使われる用語が中立的なものに変更されました。具体的には以下のような単語の置き換えが行われています。

* マスタ (master) → メイン (main)、リーダ (leader)、プライマリ (primary)
* スレーブ (slave) → レプリカ (replica)、スタンバイ (standby)
* ホワイト (white) → 読み取り (read)、キャッシュされる (cache safe)
* ブラック (black) → 書き込み (write)、プライマリに送信される (primary routing)、キャッシュされない (cache unsafe)

この変更に伴って、ネイティブレプリケーションでは、バージョン 4.1 以前ではバックエンドの 1 台目をマスタ (master)、それ以外をスレーブ (slave) と呼んでいましたが、バージョン 4.2 ではマスタをメイン (main)、スレーブをレプリカ (replica) と呼ばれるようになりました。

また、Watchdog では、バージョン 4.1 以前では仮想 IP アドレスを持つノードをマスタ (master)、それ以外をスタンバイ (standby) と呼んでいましたが、バージョン 4.2 ではスタンバイの呼び方は変わらず、マスタをリーダ (leader) と呼ばれるようになりました。

バージョン 4.1 以前と 4.2 とのパラメータの違いは以下のとおりです。

* `backend_flag# = ALWAYS_MASTER` → `ALWAYS_PRIMARY`
* `white_function_list` → `read_only_function_list`
* `black_function_list` → `write_function_list`
* `black_query_pattern_list` → `primary_routing_query_pattern_list`
* `follow_master_command` → `follow_primary_command`
* `relcache_query_target = master` → `primary`
* `white_memqcache_table_list` → `cache_safe_memqcache_table_list`
* `black_memqcache_table_list` → `cache_unsafe_memqcache_table_list`

PCP コマンドの出力もバージョン 4.1 以前と 4.2 で異なるので、出力に応じて処理を分けている場合には注意が必要です。例えば、バージョン 4.1 以前ではバックエンドの役割は `master`/`slave`、Watchdog は `MASTER`/`STANDBY` と出力されます。

```shell-session:バージョン&nbsp;4.1&nbsp;以前
$ for n in $(seq 0 2); do pcp_node_info -w $n; done
node-0 5432 1 0.333333 up master 0   2020-12-19 08:57:59
node-1 5432 1 0.333333 up slave 0 streaming async 2020-12-19 08:57:59
node-2 5432 1 0.333333 up slave 0 streaming async 2020-12-19 08:57:59

$ pcp_watchdog_info -w
3 YES node-0:9999 Linux node-0.example.com node-0

node-0:9999 Linux node-0.example.com node-0 9999 9000 7 MASTER
node-1:9999 Linux node-1.example.com node-1 9999 9000 7 STANDBY
node-2:9999 Linux node-2.example.com node-2 9999 9000 4 STANDBY
```

バージョン 4.2 ではバックエンドの役割は `primary`/`standby`、Watchdog は `LEADER`/`STANDBY` と出力されます。

```shell-session:バージョン&nbsp;4.2
$ for n in $(seq 0 2); do pcp_node_info -w $n; done
node-0 5432 1 0.333333 up primary 0   2020-12-19 09:19:36
node-1 5432 1 0.333333 up standby 0 streaming async 2020-12-19 09:19:36
node-2 5432 1 0.333333 up standby 0 streaming async 2020-12-19 09:19:36

$ pcp_watchdog_info -w
3 YES node-0:9999 Linux node-0.example.com node-0

node-0:9999 Linux node-0.example.com node-0 9999 9000 4 LEADER
node-1:9999 Linux node-1.example.com node-1 9999 9000 7 STANDBY
node-2:9999 Linux node-2.example.com node-2 9999 9000 7 STANDBY
```
# その他の変更点

その他のバージョン 4.2 の変更点としては、クライアントと Pgpool-II 間での LDAP 認証への対応や、SSL 関連のパラメータとして、SSL サーバ証明書失効リスト (CRL) のファイルを設定する `ssl_crl_file`、秘密鍵などの SSL ファイルの復号時にパスフレーズの入力が必要な場合に起動される外部コマンドを設定する `ssl_passphrase_command` の追加、PostgreSQL 13 の SQL パーサ取り込みがあります。

# おわりに

Pgpool-II 4.2 の紹介はこれでおわりです。

バージョン 4.2 への移行にあたり、パラメータ名やコマンド出力が変更になるのは少し面倒ですが、それ以上に Watchdog の設定をノード間で共通化できるのは管理がしやすくなります。また、PCP コマンドで設定ファイルの再読み込みが全ノードでまとめてできるようになりましたが、パラメータの変更や Pgpool-II の再起動もまとめてできるようになれば、さらに管理がしやすくなりそうですね。