ロジカルレプリケーションを用いたPostgreSQLのバージョンアップ

# 1. はじめに
PostgreSQL11がまもなくリリースされますね。
ロジカルレプリケーションが実装され、2世代目になるということで、今回はロジカルレプリケーションを用いたPostgreSQLのバージョンアップの手順を試してみようと思います。

## 前提
- クライアントから定期的にINSERTがされ続けている状態でバージョンアップの作業を行う
    - サービス停止時間はなるべく短くする
- クライアントからのリクエストは新設のマシンで可動するPG11で処理を継続させる

## 実施概要
バージョンアップに必要な作業のイメージは以下の通り

1. 初期状態
  - クライアントからの更新処理は定期的に飛んでいる状態
  - ![image.png](https://qiita-image-store.s3.amazonaws.com/0/125929/f65368c3-ffc9-0e1c-7c05-c62c5c255492.png)
1. PG10のスキーマをダンプしてPG11にリストアする
  - スキーマだけのダンプなので、短時間で終わるはず。。。
  - ![image.png](https://qiita-image-store.s3.amazonaws.com/0/125929/f1655c38-db04-e3a5-74ba-3833682c4ce7.png)
1. PG10とPG11でロジカルレプリケーションを行い、PG11のデータとPG10のデータを同期させる
  - はじめにデータのコピーが実施されるので、テーブルのサイズによっては同期に時間が掛かる可能性がある。
  - ![image.png](https://qiita-image-store.s3.amazonaws.com/0/125929/9b51ad6f-0d6e-c455-b14c-34df2635b1ef.png)
1. データが最新化された後、VIPを張り替える
  - ![image.png](https://qiita-image-store.s3.amazonaws.com/0/125929/f87ce6c1-f644-7d87-17d1-de12c88bfcda.png)

# 2. 実施手順

## 2.1. 初期データ投入
手順の確認のため、PG10に以下の手順でデータを挿入する

```
$ pg_ctl -D ~/10/data start
$ createdb orenodb
$ psql orenodb
psql (10.5)
Type "help" for help.

orenodb=# CREATE TABLE foo(t timestamp, v varchar);
CREATE TABLE
-- 初期データ投入
orenodb=# INSERT INTO foo VALUES (now(), md5(now()::text));
INSERT 0 1
-- データの確認
orenodb=# SELECT * FROM foo;
             t              |                v                 
----------------------------+----------------------------------
 2018-09-16 22:19:40.908432 | 5e88f2e60e04491e3f477dfca4cb0cc5
(1 row)
-- 以降も、以下のクエリを定期的に実行するようにスクリプトを仕掛けておき、データに抜けが出ないか確認する
orenodb=# INSERT INTO foo VALUES (now(), md5(now()::text))
```

## 2.2. PG10側（パブリッシャ）の準備
ロジカルレプリケーションを行うにあたり、以下の設定、手順が必要になります。

- 設定ファイル

デフォルトでは「replica」ですが、ロジカルレプリケーションを実施する場合は「logical」である必要があります。
他の値はデフォルト値で問題ありません。

```postgresql.conf
wal_level = logical
```

また、PG11側からのアクセスを許容する必要があるので、以下のような設定を行い、サブスクライバからの接続を許容する設定が必要です。

```
host    all        postgres        192.168.1.151/32         trust
```

- パブリケーションの定義

```
$ psql orenodb -h 192.168.1.150 -c "CREATE PUBLICATION orenopub FOR ALL TABLES"
```

## 2.3. PG11側（サブスクライバ）の準備
サブスクライバ側は、postgresql.confのデフォルトのパラメータでもロジカルレプリケーションを実施できる状態になっているので、特に何かを設定する必要はありません。

## 2.4. スキーマのダンプ/リストア
- スキーマ情報をダンプする（PG10で実施）

```
$ pg_dump -Fc -s -h 192.168.1.150 -p 5432 -U postgres orenodb > schema.dump
```

- ダンプしたファイルをpg_restoreでリストアし、必要なオブジェクトが作成されていることを確認する（PG11で実施）

```
// PostgreSQL11を起動する
$ pg_ctl -D ~/11/data start
// restore実施
$ pg_restore -s -h 192.168.1.151 -p 5432 -U postgres -d orenodb schema.dump
// PG10で定義していたテーブルがPG11で作成されていることを確認
$ psql orenodb -c "\dt"
        List of relations
 Schema | Name | Type  |  Owner   
--------+------+-------+----------
 public | foo  | table | postgres
(1 row)
```

## 2.5. ロジカルレプリケーションを開始する
- サブスクリプションを定義する（PG11側）

```
$ psql orenodb -h 192.168.1.151 -c "CREATE SUBSCRIPTION orenosub CONNECTION 'dbname=orenodb host=yama' PUBLICATION orenopub"
```

※CONNECTION の設定については[マニュアル](https://www.postgresql.jp/document/10/html/libpq-connect.html#LIBPQ-CONNSTRING)を参照ください。

- ログの確認

パブリッシャでは以下のようなログが出力される

```
LOG:  starting logical decoding for slot "orenosub"
DETAIL:  streaming transactions committing after 0/166A988, reading WAL from 0/166A950
```

サブスクライバでは以下のようなログが出力される

```
LOG:  logical replication apply worker for subscription "orenosub" has started
```

- プロセス確認

パブリッシャ（PG10側）では以下のプロセスが起動したことが確認できる

```
postgres   760  0.0  0.5 295744  5196 ?        Ss   12:11   0:00 postgres: wal sender process postgres 192.168.1.151(50048) idle
```

サブスクライバ（PG11側）では以下のプロセスが起動したことが確認できる

```
postgres   758  0.0  0.5 300832  6096 ?        Ss   12:11   0:00 postgres: logical replication worker for subscription 16398
```

## 2.6. データが同期するのを待つ
- 同期レプリケーションの設定を行う（PG10）

非同期レプリケーションの状態では、PG10からPG11に接続先を変更する際に、タイミングによってはデータが欠損する恐れがあるので、同期レプリケーションの設定に変更する。

```postgresql.conf
# 定義したサブスクリプションの名前を指定する。
synchronous_standby_names = 'orenosub'
```

```
// 設定ファイルをreloadし、反映させる
$ pg_ctl -D ~/10/data reload
```

- データの送信位置、受信位置を確認し、データが最新状態に追いついたことを確認する

PG10側の送信位置、PG11の受信位置が「0/16A2908」となっていることから、2つのDBの同期が取れたことが確認できる。

```
// PG10側での確認結果
$ psql orenodb -c "SELECT pid,application_name,sent_lsn,write_lsn,flush_lsn,replay_lsn,sync_state FROM pg_stat_replication"
 pid  | application_name | sent_lsn  | write_lsn | flush_lsn | replay_lsn | sync_state 
------+------------------+-----------+-----------+-----------+------------+------------
 1268 | orenosub         | 0/16A2908 | 0/16A2908 | 0/16A2908 | 0/16A2908  | sync
(1 row)
```

```
// PG11側での確認結果
$ psql orenodb -c "SELECT pid,subname,received_lsn,latest_end_lsn FROM pg_stat_subscription"
 pid  | subname  | received_lsn | latest_end_lsn 
------+----------+--------------+----------------
 1057 | orenosub | 0/16A2908    | 0/16A2908
(1 row)
```

## 2.7. VIPを張り替えて作業は完了
- 一時的に更新処理を止め、VIPを張り替える
    - 本手順のサービス停止時間はこの期間になる。
- VIPを張り替えたあと、更新を再開することでメジャーバージョンアップ自体の作業は完了。
- あとはロジカルレプリケーションの設定を解除して、PG10側を片付けるだけです。

# さいごに
一応、今回のデータでは無事にメジャーバージョンの変更を行うことができた。ただ、PostgreSQL10のロジカルレプリケーションには制約があるため、データによっては今回の手段は実施できないケースがあるような気がしている。

また、今回試している中でいくつか疑問が出てきたので、調べたら補足していこうと思う。