PostgreSQL 9.5 から提供されたpg_rewindを使ってみる

# pg_rewindとは

Streaming Replicationを使ってマスタ-スタンバイ構成のPostgreSQLを運用している場合、フェイルオーバやスイッチオーバが発生してマスタが切り替わった後に、古いマスタを再度組み込む際に役立つコマンドです。

http://www.postgresql.org/docs/9.5/interactive/app-pgrewind.html

百聞は一見に如かず。
レッツトライ。

# Streaming Replication構成の作成

## 1号機の設定

postgresql.confに以下を記載します。

pg_rewindのために```wal_log_hints```を有効にしておきます。データチェックサム有効で代用できるみたいですが今回はこれで。

```
listen_addresses = '*'
wal_level = hot_standby
max_wal_senders = 3
wal_keep_segments = 30
max_replication_slots = 3
hot_standby = on

logging_collector = on
log_line_prefix = '[%m]'

wal_log_hints = on 
```

pg_hba.confに1号機、2号機間でpg_basebackupで接続できるように設定します。
また、pg_rewindを実行するために、replication権限でないスーパーユーザによる通常の接続も許可しておきます。

```
host    postgres        postgres        192.168.56.101/32       trust
host    postgres        postgres        192.168.56.102/32       trust
host    replication     postgres        192.168.56.101/32       trust
host    replication     postgres        192.168.56.102/32       trust
```

PostgreSQLを起動して、レプリケーション・スロットを作成します。

```
$ pg_ctl start

$ psql
psql (9.5.0)
Type "help" for help.

postgres=# SELECT * FROM pg_create_physical_replication_slot('slave_slot');
 slot_name  | xlog_position 
------------+---------------
 slave_slot | 
(1 row)
```

## 2号機の設定

pg_basebackupでデータをコピーします。

```
$ pg_basebackup -h 192.168.56.102 -D 9.5
NOTICE:  pg_stop_backup complete, all required WAL segments have been archived
```

スタンバイとして起動するために、recovery.confを作成します。

```
$ cp /usr/local/pgsql/9.5/share/recovery.conf.sample recovery.conf
```

以下を記載します。

```
standby_mode = on
primary_conninfo = 'host=192.168.56.102 application_name=slave1'
primary_slot_name = 'slave_slot'
```

PostgreSQLを起動します。これで非同期レプリケーション構成になります。

```
$ pg_ctl start
```

## 同期レプリケーション設定

1号機にてpostgresql.confに以下を追記します。

```
synchronous_standby_names = 'slave1'
```

設定ファイルを更新すれば、同期レプリケーション構成になります。

```
$ pg_ctl reload
$ psql
psql (9.5.0)
Type "help" for help.

postgres=# SELECT * FROM pg_stat_replication ;
-[ RECORD 1 ]----+------------------------------
pid              | 4905
usesysid         | 10
usename          | postgres
application_name | slave1
client_addr      | 192.168.56.101
client_hostname  | 
client_port      | 44976
backend_start    | 2016-01-30 22:26:49.279467+09
backend_xmin     | 
state            | streaming
sent_location    | 0/3000220
write_location   | 0/3000220
flush_location   | 0/3000220
replay_location  | 0/3000220
sync_priority    | 1
sync_state       | sync
```

# フェイルオーバ発生

pg_rewindを使う状況にするため、1号機から2号機にフェイルオーバさせます。

1号機のPostgreSQLをシャットダウン。

```
$ pg_ctl stop
waiting for server to shut down.... done
server stopped
```

2号機をマスタに昇格させます。

```
$ pg_ctl promote
server promoting
```

## 1号機のクラスタ再組み込み


pg_rewindを使わない場合、pg_basebackupで2号機からデータを全コピーする必要がありました。
つまり、新しくスタンバイ機を作成する手順と同一です。

しかし、1号機にはもともとマスタとして動作していた時のデータが残っています。これを流用して、2号機にスタンバイとして追随するために必要なデータだけを取ってきてくれるのがpg_rewindです。

データベースのサイズが大きい場合、全データをコピーするpg_basebackupに比べて格段に効率的で時間も短く済みます。


正常停止後のデータベースに対してpg_rewindを実行します。
ちなみに、```-n```オプションをつけるとドライランになるので、これでオプションが正しく設定できているか確かめると便利です。


```
$ pg_rewind -D pg_data/9.5/ --source-server="host=192.168.56.101"
servers diverged at WAL position 0/3000220 on timeline 1
rewinding from last common checkpoint at 0/3000178 on timeline 1
Done!
```

あとは、recovery.conを作って、PostgreSQLを起動して、同期レプリケーション構成にするだけです。


# ハマリどころ

## pg_rewindはreplication権限以外に、スーパユーザによる通常の接続権限も要求する

```
$ pg_rewind -D pg_data/9.5/ --source-server="host=192.168.56.101"

could not connect to server: FATAL:  no pg_hba.conf entry for host "192.168.56.102", user "postgres", database "postgres"
Failure, exiting
```

pg_hba.confに忘れず設定しておきましょう。


## 旧マスタのデータベースは正常停止しておく必要がある

Streaming ReplicationによるHAクラスタを組んでいる場合、フェイルオーバ発生時、旧マスタはimmediateモードで停止している場合が多いと思います。

その状態でpg_rewindを実行しても以下のようなメッセージが出て失敗します。

```
$ pg_rewind -D pg_data/9.5/ --source-server="host=192.168.56.101"
                                                                                              
target server must be shut down cleanly                                                       
Failure, exiting 
```

そのため、一度PostgreSQLを起動して正常停止させます。

```
$ pg_ctl start
$ pg_ctl stop
```

## レプリケーション・スロットは新たに作成しなおす必要がある

これは、pg_rewindには直接関係ないです。
というかレプリケーション・スロットの仕様なので、pg_basebackupを使って新たにスタンバイを組み込む場合も同じです。

レプリケーション・スロットを使っている場合、新しいマスタにはスロットが存在していないので、再度作成する必要があります。

## postgresql.confをシンボリックリンクで運用している場合、一度リンクを削除する必要がある

同期・非同期レプリケーションの切り替えに```synchronous_standby_names```を設定したファイルとしていないファイルを両方用意して、postgresql.confというシンボリックリンクでどちらを利用するか切り替えるような運用をしている場合、pg_rewindが以下のようなエラーを吐きます。

```
$ pg_rewind -D pg_data/9.5/ --source-server="host=192.168.56.101" -n
servers diverged at WAL position 0/E000060 on timeline 5
rewinding from last common checkpoint at 0/D000060 on timeline 5

"pg_data/9.5//postgresql.conf" is not a regular file
Failure, exiting
```

pg_rewind対象の停止済みのデータベース領域から一度シンボリックリンクを削除してあげれば大丈夫です。スタンバイ再組み込みの時にどのみち新たに作成しなおすので問題はないです。

以上！