pg_rman の増分バックアップは前回バックアップからの差分だった！もう迷わない！

#はじめに
この記事は「NTTテクノクロス Advent Calendar 2021」の11日目の記事です。

こんにちは！NTTテクノクロスの中村です。

本記事は、オープンソースデータベース PostgreSQL のバックアップツールである、「pg_rman」を使用する人向けに記載しました。
わざわざ検証することなく、疑問点を解消していただければ嬉しいです。

# pg_rman の増分バックアップは、累積なのか、差分なのか？
pg_rman は差分バックアップが取得可能な、PostgreSQL のバックアップツールです。
バックアップの世代管理機能もあるため、管理の手間を少なくすることもできます。

ただ、pg_rman のドキュメントには「差分」という表現はなく、「増分」という表現になっています。

この「増分」というものが、「フルバックアップ」からの「累積」の増分なのか、それとも直前の「増分バックアップ」からの「差分」なのかは、pg_rman ドキュメントに記載がありません。
また、pg_rman を紹介している各サイトにも明記されていないため、ググっても答えが出てこない…
<font color=red>（そもそも累積とか差分、というイメージは商用DBMSの差分バックアップ機能にあったからなのですが、使う人によっても異なるので明確に定義しづらい、というのもあります）</font>

というわけで、結局のところどっちなのか、確認してみました。

# pg_rman の増分バックアップは直前のバックアップからの差分！
pg_rman の増分バックアップには、直前に取得した「フルバックアップ」もしくは「増分バックアップ」からの 変更差分 が含まれます。
つまり、pg_rman のリカバリには、「フルバックアップ」＋「フルバックアップ後に取得した、全ての増分バックアップ」が必要になる、ということですね。

リカバリのパターンとして例えば、

:::note info
　フルバックアップ ＋ 増分１ ＋ 増分２ ＋ 増分３
:::
はOKですが、

:::note alert
　フルバックアップ ＋ 増分３
:::
みたいな組み合わせではリカバリできない、ということですね。
![pg_rmanバックアップイメージ1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318431/aea52a73-7222-44bf-b8f7-92deffc4c76a.png)

## pg_rman でリカバリしてみる
それでは実際に、フルバックアップ、増分１、増分２、増分３のバックアップを取得し、そのバックアップを使ってリカバリを行ってみましょう。

①「フルバックアップ」＋「増分１」＋「増分２」＋「増分３」
②「フルバックアップ」＋「増分３」
の組み合わせでリカバリできるかを見ていきます。

もろもろの設定は公式ドキュメントに従って実施済みであるとします。
https://ossc-db.github.io/pg_rman/index-ja.html

今回は、手元にあった環境で実施します。
・CentOS 8.3
・PostgreSQL 13.1
・pg_rman 1.3.13

### フルバックアップ＋増分バックアップを3回実施した状態
まずはバックアップから。
以下は、initdb直後にフルバックアップを取得し、その後 pgbench -i -s 10 を実行して増分バックアップを取得、を3回繰り返した結果です。
フルバックアップの「FULL」が1個と、増分バックアップの「INCR」が3個あることがわかります。
下が古く、上が新しいバックアップです。

```sh
[postgres@localhost rman_catalog]$ pg_rman show -B /var/lib/pgsql/rman_catalog                      
=====================================================================
 StartTime           EndTime              Mode    Size   TLI  Status
=====================================================================
2021-12-07 05:03:12  2021-12-07 05:03:15  INCR   309MB     1  OK
2021-12-07 05:02:25  2021-12-07 05:02:29  INCR   309MB     1  OK
2021-12-07 05:01:21  2021-12-07 05:01:24  INCR   304MB     1  OK
2021-12-07 05:00:38  2021-12-07 05:00:40  FULL    50MB     1  OK
```

pg_rmanのカタログが格納されているディレクトリの中を見ると、「20211207」というフォルダがありますね。これがバックアップしたファイルの実体が格納されているディレクトリです。

```sh
[postgres@localhost rman_catalog]$ ls -ltr
合計 8
drwx------. 2 postgres postgres  6 12月  7 04:58 timeline_history
-rw-r--r--. 1 postgres postgres 40 12月  7 04:58 system_identifier
-rw-r--r--. 1 postgres postgres 85 12月  7 04:58 pg_rman.ini
drwx------. 4 postgres postgres 34 12月  7 04:58 backup
drwx------. 6 postgres postgres 62 12月  7 05:03 20211207
[postgres@localhost rman_catalog]$ cd 20211207/
[postgres@localhost 20211207]$ ls -ltr
合計 0
drwx------. 5 postgres postgres 133 12月  7 05:00 050038
drwx------. 5 postgres postgres 133 12月  7 05:01 050121
drwx------. 5 postgres postgres 133 12月  7 05:02 050225
drwx------. 5 postgres postgres 133 12月  7 05:03 050312
```

show コマンドの「StartTime」の値がそのままディレクトリ名になっているのがわかりますね。
「050038」ディレクトリにはフルバックアップが、その他のディレクトリには増分バックアップが、それぞれ格納されています。

### ①全ての増分バックアップがある状態でリカバリを実施
まずは、全ての増分バックアップがある状態で、正常にリカバリできることを確認します。
「フルバックアップ」＋「増分１」＋「増分２」＋「増分３」
でのリカバリですね。

起動中のPostgreSQLは停止し、データベースクラスタのディレクトリを削除してからリストアコマンドを実施します。

```sh
[postgres@localhost 13]$ pg_rman restore -B /var/lib/pgsql/rman_catalog
WARNING: pg_controldata file "/var/lib/pgsql/13/data/global/pg_control" does not exist
INFO: the recovery target timeline ID is not given
INFO: use timeline ID of latest full backup as recovery target: 1
INFO: calculating timeline branches to be used to recovery target point
INFO: searching latest full backup which can be used as restore start point
INFO: found the full backup can be used as base in recovery: "2021-12-07 05:00:38"
INFO: copying online WAL files and server log files
INFO: clearing restore destination
INFO: validate: "2021-12-07 05:00:38" backup and archive log files by SIZE
INFO: backup "2021-12-07 05:00:38" is valid
INFO: restoring database files from the full mode backup "2021-12-07 05:00:38"
INFO: searching incremental backup to be restored
INFO: validate: "2021-12-07 05:01:21" backup and archive log files by SIZE
INFO: backup "2021-12-07 05:01:21" is valid
INFO: restoring database files from the incremental mode backup "2021-12-07 05:01:21"
INFO: validate: "2021-12-07 05:02:25" backup and archive log files by SIZE
INFO: backup "2021-12-07 05:02:25" is valid
INFO: restoring database files from the incremental mode backup "2021-12-07 05:02:25"
INFO: validate: "2021-12-07 05:03:12" backup and archive log files by SIZE
INFO: backup "2021-12-07 05:03:12" is valid
INFO: restoring database files from the incremental mode backup "2021-12-07 05:03:12"
INFO: searching backup which contained archived WAL files to be restored
INFO: backup "2021-12-07 05:03:12" is valid
INFO: restoring WAL files from backup "2021-12-07 05:03:12"
INFO: restoring online WAL files and server log files
INFO: create pg_rman_recovery.conf for recovery-related parameters.
INFO: remove an 'include' directive added by pg_rman in postgresql.conf if exists
INFO: append an 'include' directive in postgresql.conf for pg_rman_recovery.conf
INFO: generating recovery.signal
INFO: removing standby.signal if exists to restore as primary
INFO: restore complete
HINT: Recovery will start automatically when the PostgreSQL server is started. After the recovery is done, we recommend to remove recovery-related parameters configured by pg_rman.
```
正常に終了していますね！
ログをかいつまんで説明すると、
（１）最初に「INFO: found the full backup can be used as base in recovery: "2021-12-07 05:00:38"」でフルバックアップを復元
（２）次に「INFO: searching incremental backup to be restored」で復元に必要な増分バックアップが存在しているか確認を開始
（３）次に「INFO: restoring database files from the incremental mode backup "2021-12-07 05:01:21"」のように増分バックアップを復元（順次３回行われている）
（４）増分バックアップ復元後に「backup "2021-12-07 05:03:12" is valid」で復元したデータベースクラスタの検証(Validation)を実施
（５）最後に諸々の後始末を行って「INFO: restore complete」と出力し、復元を完了
という流れで正常にしています。

```sh
[postgres@localhost 13]$ pg_ctl start
サーバの起動完了を待っています....2021-12-07 05:10:04.811 EST [193629] LOG:  redirecting log output to logging collector process
2021-12-07 05:10:04.811 EST [193629] HINT:  Future log output will appear in directory "log".
完了
サーバ起動完了
```
復元したデータベースクラスタに対し、pg_ctl start で無事起動することができました！
これで、「フルバックアップ」＋「増分１」＋「増分２」＋「増分３」の状態では問題なくリカバリできることが確認できました。

### ②途中の増分バックアップを削除してリカバリを実施
それでは、次に「増分１」と「増分２」のバックアップが格納されているディレクトリを削除（リネーム）して、「フルバックアップ」＋「増分３」だけでリカバリできるか確認してみましょう。

```sh
[postgres@localhost 20211207]$ mv 050121 050121_
[postgres@localhost 20211207]$ mv 050225 050225_
[postgres@localhost 20211207]$ ls -ltr
合計 0
drwx------. 5 postgres postgres 133 12月  7 05:00 050038
drwx------. 5 postgres postgres 133 12月  7 05:01 050121_
drwx------. 5 postgres postgres 133 12月  7 05:02 050225_
drwx------. 5 postgres postgres 133 12月  7 05:03 050312
```
①と同じく、
起動中のPostgreSQLは停止し、データベースクラスタのディレクトリを削除してからリストアコマンドを実施します。

```sh
[postgres@localhost 20211207]$ pg_rman restore -B /var/lib/pgsql/rman_catalog
WARNING: pg_controldata file "/var/lib/pgsql/13/data/global/pg_control" does not exist
INFO: the recovery target timeline ID is not given
INFO: use timeline ID of latest full backup as recovery target: 1
INFO: calculating timeline branches to be used to recovery target point
INFO: searching latest full backup which can be used as restore start point
INFO: found the full backup can be used as base in recovery: "2021-12-07 05:00:38"
INFO: copying online WAL files and server log files
INFO: clearing restore destination
INFO: validate: "2021-12-07 05:00:38" backup and archive log files by SIZE
INFO: backup "2021-12-07 05:00:38" is valid
INFO: restoring database files from the full mode backup "2021-12-07 05:00:38"
INFO: searching incremental backup to be restored
INFO: validate: "2021-12-07 05:01:21" backup and archive log files by SIZE
ERROR: could not open "/var/lib/pgsql/rman_catalog/20211207/050121/file_database.txt": そのようなファイルやディレクトリはありません
```
はい、無事に？エラーになりました。
先ほどリネームしたディレクトリの下のファイルを参照しようとして怒られていますね。

こちらもログをかいつまんで説明すると、
（１）最初に「INFO: found the full backup can be used as base in recovery: "2021-12-07 05:00:38"」でフルバックアップを復元
（２）次に「INFO: searching incremental backup to be restored」で復元に必要な増分バックアップが存在しているか確認を開始
（３）正常なケースでは「INFO: backup "2021-12-07 05:01:21" is valid」が出るべきところで、「ERROR: could not open "/var/lib/pgsql/rman_catalog/20211207/050121/file_database.txt": そのようなファイルやディレクトリはありません」を出力

増分バックアップが変更差分の累積であれば、フルバックアップと最後の増分バックアップだけでリカバリできるはずですが、途中の増分バックアップのファイルを参照していることから、「フルバックアップ」＋「増分３」だけでリカバリはできない、ということが確認できました。

また、増分バックアップの取得前に、それぞれ pgbench -i -s 10 を実施していますが、増分バックアップのサイズはいずれも300MB程度と、ほぼ同じでした。変更差分の累積であればサイズも増加していくはずですが、サイズの面からも増分バックアップは累積ではなく、直前のフルバックアップまたは増分バックアップからの変更差分、ということが確認できました。

```sh
[postgres@localhost rman_catalog]$ pg_rman show -B /var/lib/pgsql/rman_catalog                      
=====================================================================
 StartTime           EndTime              Mode    Size   TLI  Status
=====================================================================
2021-12-07 05:03:12  2021-12-07 05:03:15  INCR   309MB     1  OK
2021-12-07 05:02:25  2021-12-07 05:02:29  INCR   309MB     1  OK
2021-12-07 05:01:21  2021-12-07 05:01:24  INCR   304MB     1  OK
2021-12-07 05:00:38  2021-12-07 05:00:40  FULL    50MB     1  OK
```

# まとめ
今回は、pg_rman の増分バックアップは累積ではない、ということを確認しました。
これで、pg_rman の導入の際の疑問が一つ解消されたことと思います。
PostgreSQL で差分バックアップを行う際には必須のツールですので、導入の際の選択肢として、ぜひご活用ください！

https://www.ntt-tx.co.jp/products/osscloud/

明日は、@yuyhiraka さんです！
引き続き、NTTテクノクロス Advent Calendar 2021 をお楽しみください。