PostgreSQL 9.6 で実装されたpg_start_backupのnon-exclusive modeについて

PostgreSQL10のbeta1がリリースされましたね。

タイミングとしては、かなり今更ですが、PG96で実装されたpg_start_backupのnon-exclusiveモードを少し試していたので、備忘録として残します。

# non-exclusive mode（非排他モード）とは
ざっくり言うと、これまでのexclusive modeとは以下の点で違いがあります。

- 複数のバックアップを同時に取得できるようになった。
    - pg_start_backup中に別の人がpg_start_backupを実行できる。
- Slaveに対してpg_start_backupが実行できるようになった。
    - これまではMasterにしか実行することができなかった。
    - （Masterからのバックアップ取得は以前からpg_basebackupコマンドでもできていたので、pg_start_backupに限った話という意味。）
- pg_start_backup実行時、PGDATA配下にbackup_labelは作成されない。
    - これまではpg_start_backupで$PGDATA/backup_labelが作成され、pg_stop_backupで削除されていた。
    - backup_labelの情報はpg_stop_backup(false)実行時に出力される

という具合です。

# non-exclusive modeでのバックアップ取得方法
以降の手順は対象がマスタ、スレーブのどちらであっても手順は同じです。

## 1. psqlで接続し、pg_start_backupを実行する。
non-exclusiveモードは第三引数（exclusive）を'false'にすることで使用できます。

```
postgres=# SELECT pg_start_backup('ore_no_backup', true, false);                                                                                    
 pg_start_backup 
-----------------
 0/2000060
(1 row)
```

## 2. ベースバックアップ取得
pg_start_backup実行後、cpコマンド等で＄PGDATA配下のファイルをバックアップとして取得する。（従来と操作は変わらない。）

## 3. pg_stop_backupを実行する
pg_stop_backup(false)は、手順1と同じセッションで実行する。

```
postgres=# SELECT * FROM pg_stop_backup(false);                                                                                      
NOTICE:  pg_stop_backup complete, all required WAL segments have been archived
    lsn    |                           labelfile                           | spcmapfile 
-----------+---------------------------------------------------------------+------------
 0/6A055D0 | START WAL LOCATION: 0/2000028 (file 000000010000000000000002)+| 
           | CHECKPOINT LOCATION: 0/2000060                               +| 
           | BACKUP METHOD: streamed                                      +| 
           | BACKUP FROM: master                                          +| 
           | START TIME: 2017-05-20 10:20:38 JST                          +| 
           | LABEL: ore_no_backup                                         +| 
           |                                                               | 
(1 row)
```

|カラム|概要|
|:---|:---|
|lsn|pg_stop_backup実行時のWALの書き込み位置|
|labelfile|backup_labelとして保存する内容|
|spcmapfile|tablespace_mapとして保存する内容|

このpg_stop_backupの結果から、リカバリに必要なアーカイブファイル等を確認することができます。

## 4. ベースバックアップ（2で取得済み）をリカバリするために以下のものを用意する。
- アーカイブWAL
- backup_label
- recovery.conf

以降は例として、pg_stop_backup実行時までの状態にリストアする場合に必要なものを記載する。

### 4-1. アーカイブWALのバックアップを取得する。
ベースバックアップのリカバリに必要なアーカイブWALは、手順3のlabelfile,lsn（pg_stop_backup実行時の出力結果）から判断できる。今回だとSTART WAL LOCATIONで書かれている000000010000000000000002ファイルから、000000010000000000000006（lsn：0/6A055D0より判断）が対象になるので、以下のアーカイブWALをバックアップとして確保しておきます。

```
$ ll /tmp/back_arch/
total 81920
-rw------- 1 postgres postgres 16777216  5月 20 10:28 000000010000000000000002
-rw------- 1 postgres postgres 16777216  5月 20 10:28 000000010000000000000003
-rw------- 1 postgres postgres 16777216  5月 20 10:28 000000010000000000000004
-rw------- 1 postgres postgres 16777216  5月 20 10:28 000000010000000000000005
-rw------- 1 postgres postgres 16777216  5月 20 10:28 000000010000000000000006
```

LSNから対象のWALファイル名を調べるコマンドも用意されているので、それを使うのもありです。

```
$ psql postgres -c "SELECT pg_walfile_name('0/6A055D0')"
     pg_walfile_name      
--------------------------
 000000020000000000000006
(1 row)
```

ちなみに、PG96まではpg_xlogfile_name()でしたが、PG10からは上記のように関数名が変わっているので注意。

### 4-2. backup_labelを作成する。
こちらは、labelfileの内容をそのままコピペすればOKです。

```
$ cat $BACKUP/backup_label 
START WAL LOCATION: 0/2000028 (file 000000010000000000000002)
CHECKPOINT LOCATION: 0/2000060
BACKUP METHOD: streamed
BACKUP FROM: master
START TIME: 2017-05-20 10:20:38 JST
LABEL: ore_no_backup
```

PostgreSQLは起動時にbackup_labelの中身からリカバリを開始する場所を確認しているので、このファイルがないとバックアップをリストアすることはできません。

### 4-3. recovery.confを作成する。
restore_commandには、バックアップしたアーカイブWALのパスを指定しておきます。

```
$ cat $BACKUP/recovery.conf 
recovery_target_timeline='latest'
restore_command='cp /tmp/back_arch/%f %p'
```

## おわり
以上で、バックアップで必要な操作（ベースバックアップ取得、リカバリに必要なファイルの用意）が全て完了しました。

# ちなみに
コミュニティのwikiとかを見ると、将来的に従来のexclusiveモードは廃止の方針とのことです。
今のうちに新しいものは（若干古いが）ちゃんとキャッチアップしておこう！

あと、non-exclusiveモードはpsql の-cオプションで操作することができません。オンラインバックアップの操作（pg_start_backup/pg_stop_backup）は同じセッション内で行う必要があるので注意しよう。従来は-cオプションで操作できた。

# 追記（2017/6/21）
スタンバイでpg_stop_backup(false)を実行した場合、従来のものと挙動が異なるので注意！！！

- マスタで実行した場合
  - WALをスイッチを行い、recoveryに必要なWALのアーカイブの完了を待つ。（従来通りの挙動）
- スタンバイで実行した場合
  - WALのスイッチを行うが、マスタでのWALのアーカイブ完了は待たない。
  - archive_mode = always 設定時、スタンバイでのアーカイブも当然待たない。

つまり、スタンバイでpg_stop_backup(false)を実行した場合、アーカイブが失敗していてもstopは成功するため、ちゃんと必要なWALがアーカイブされているかチェックしないと***危険！！！***ということ。

これって仕様としてどうなんだ。。。せめてマニュアルには書いといてほしいな。

# 更に追記
PG10では、pg_stop_backupにもう一つパラメータが追加される。

> pg_stop_backup(exclusive boolean [, wait_for_archive boolean ])


マニュアルを読むとWALアーカイブを待つかどうか制御できるものらしいが、ソースを見る限り、スタンバイで実行する場合は当然待たない。（というか待てない。）