Aurora for PostgreSQL の Blue/Green を利用したメンテナンスをやってみた

# はじめに

以前 Aurora for PostgreSQLのフルマネージドの Blue/Green デプロイを利用して 13.2 ⇒ 15.4 バージョンアップを行う形を試してみました。（Blue/Greenデプロイの説明はそちらに記載がありますので、基本的なことを確認したければ、以下を参照ください）
- [Aurora for PostgreSQL の フルマネージド Blue/Green デプロイを試してみた](https://qiita.com/asahide/items/0914ac4132169220ae5f)

今回は Blue/Green を利用して 稼働中のデータベースに対してメンテナンス（テーブル追加・カラム追加）を行ってみます。
具体的には、Blue/Greenデプロイを作成したAurora for postgreSQL環境に対して、Green環境にメンテナンス（テーブル追加・カラム追加）を行った後にその環境へ切り替えを行う事で、実行環境(Blue環境)への負荷やサービス中断を発生させることなくメンテを行う事ができる、というものになります。

という事でやってみます。

# Aurora for PostgreSQL Blue/Green 環境の作成（再掲）

rds.logical_replicationを設定しておく必要がある事を忘れずに。

「アクション」から「ブルー/グリーンデプロイの作成 -新規」を選択してBlue/Greenデプロイを作成します
![00-bgtest-deploy.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/bf6eeaf8-d705-1cd5-bbb2-ab9eae62ab7b.png)

今回は前述のとおり、グリーン側のエンジンバージョンはブルー側と同じ 13.12 を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/f504974a-6fe0-f982-4750-41fdf867ef7c.png)

バージョンが同じだとグリーン環境のバージョンアップが行われないため、高速にグリーン環境を作成することができます。（違うバージョンでももちろんメンテナンスは可能です）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/3bde11ee-3fc1-f313-accf-cb79572f6208.png)

# グリーン環境のメンテナンス

#### Blue/Greenの状態確認

作成されたグリーン環境の状態は以下のようになります。
ブルー環境と同様にtesttblというテーブルが一つあり、idとdateという二つのカラムが確認できます。

```SQL:グリーン環境の状態
bgtest=> \dt
          List of relations
 Schema |  Name   | Type  |  Owner
--------+---------+-------+----------
 public | testtbl | table | postgres
(1 row)

bgtest=> \d testtbl
                        Table "public.testtbl"
 Column |            Type             | Collation | Nullable | Default
--------+-----------------------------+-----------+----------+---------
 id     | integer                     |           | not null |
 date   | timestamp without time zone |           |          |
Indexes:
    "test_pk" PRIMARY KEY, btree (id)
```

ブルー環境のtesttblテーブルにデータを挿入すると、グリーン環境にもデータがレプリケートされます。

```SQL:ブルー環境のデータ
bgtest=> select * from testtbl order by id;
 id |            date
----+----------------------------
  1 | 2024-03-14 01:37:36.894382
  2 | 2024-03-14 01:37:53.47027
  3 | 2024-03-14 01:37:54.501774
  4 | 2024-03-14 01:37:55.530055
  5 | 2024-03-14 01:37:56.560074
  6 | 2024-03-14 01:37:57.601018
(6 rows)
```
両方とも同じデータであることが確認できます。
```SQL:グリーン環境へのデータレプリケーションの確認
bgtest=> select * from testtbl order by id;
 id |            date
----+----------------------------
  1 | 2024-03-14 01:37:36.894382
  2 | 2024-03-14 01:37:53.47027
  3 | 2024-03-14 01:37:54.501774
  4 | 2024-03-14 01:37:55.530055
  5 | 2024-03-14 01:37:56.560074
  6 | 2024-03-14 01:37:57.601018
(6 rows)
```

#### グリーン環境へのメンテナンス実施（失敗）

グリーン環境に接続し、先ほどのtesttblへカラム追加を行ってみますが、エラーが出て失敗してしまいます。
```SQL:グリーン環境のテーブルへのカラム追加
$ psql -h bgdb01-cluster-green-w1l5mu.cluster-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com -p 5432 -U postgres -d bgtest
psql (13.7, server 13.12)
SSL connection (protocol: TLSv1.2, cipher: AES128-SHA256, bits: 128, compression: off)
Type "help" for help.

bgtest=> alter table testtbl add column testcol varchar(20);
ERROR:  cannot execute ALTER TABLE in a read-only transaction
```

マニュアルを確認すると、[「ブルー/グリーンデプロイを作成すると、グリーン環境の DB クラスターはデフォルトで書き込み操作を許可します」](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/blue-green-deployments-overview.html) となっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/c4962f65-6810-2b36-b3a2-9fb7b82bcb65.png)

おかしいな、と思って [英語版のマニュアル](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/blue-green-deployments-overview.html#blue-green-deployments-major-steps) も確認すると以下のような記載となっており、グリーン環境がデフォルトで書き込み可能なのはMySQLのみになるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/e9eb7f8a-fca5-7ce3-25b7-d8b8b4030f8e.png)


グリーン環境ではdefault_transaction_read_onlyパラメータがデフォルトでON（1）になっていました。

```SQL: グリーン環境のRead Onlyパラメータ確認
bgtest=> select name, setting , unit from pg_settings where name ='default_transaction_read_only';
             name              | setting | unit
-------------------------------+---------+------
 default_transaction_read_only | on      |
(1 row)
```

#### グリーン環境へのメンテナンス実施（成功）

グリーン環境のRead Onlyを、新しくパラメータグループをセットすることで解除します。
パラメータグループをセットする必要があるため、グリーン環境を一度再起動する必要があります。
（もしくは、最初から default_transaction_read_only を 0 としてパラメータグループを設定しておく）

```SQL: グリーン環境のRead Onlyパラメータ確認
bgtest=> select name, setting , unit from pg_settings where name ='default_transaction_read_only';
             name              | setting | unit
-------------------------------+---------+------
 default_transaction_read_only | off     |
(1 row)
```

無事グリーン環境のRead Onlyが解除されたため、testtblへカラム追加を行います。

```SQL: グリーン環境のtesttblへのカラム追加
bgtest=> alter table testtbl add column testcol varchar(20);
ALTER TABLE
bgtest=> \d testtbl
                         Table "public.testtbl"
 Column  |            Type             | Collation | Nullable | Default
---------+-----------------------------+-----------+----------+---------
 id      | integer                     |           | not null |
 date    | timestamp without time zone |           |          |
 testcol | character varying(20)       |           |          |
Indexes:
    "test_pk" PRIMARY KEY, btree (id)

bgtest=> select * from testtbl order by id;
 id  |            date            | testcol
-----+----------------------------+---------
   1 | 2024-03-14 01:37:36.894382 |
   2 | 2024-03-14 01:37:53.47027  |
   3 | 2024-03-14 01:37:54.501774 |
   4 | 2024-03-14 01:37:55.530055 |
   5 | 2024-03-14 01:37:56.560074 |
   6 | 2024-03-14 01:37:57.601018 |
(6 rows)
 ```

#### Blue/Greenの切替

無事グリーン環境のメンテナンス（カラム追加）が完了したため、Blue/Greenの切替を実施します。
グリーン側のパラメータグループはRW用のパラメータグループになっています。（インスタンスではなくクラスタパラメータグループで設定した方が本当は良いですね）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/92988773-3c8a-1108-6a63-09504dae23aa.png)

切替が完了すると以下のように表示されます。
前回の検証時に比べるとどのインスタンスが現在のプロダクションかが視認しやすくなってますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/04508fe2-e828-b1be-06f4-875496c42236.png)

新しいブルー環境のtesttblテーブルを確認すると、カラムが追加された状態になっていることがわかります。

```SQL: 新しいブルー環境のテーブル確認
$ psql -h bgdb01.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com -p 5432 -U postgres -d bgtest
psql (13.7, server 13.12)
SSL connection (protocol: TLSv1.2, cipher: AES128-SHA256, bits: 128, compression: off)
Type "help" for help.

bgtest=> select * from testtbl where id=(select max(id) from testtbl);
 id  |            date            | testcol
-----+----------------------------+---------
 286 | 2024-03-14 02:27:26.548948 |
(1 row)

```


# まとめ

Aurora for PostgreSQLのフルマネージドなBlue/Greenデプロイを利用したメンテナンスを実行してみました。
メンテナンス自体はもちろんBlue/Greenデプロイを利用せずとも実施できますが、例えば今回実施したカラム追加はロックを確保するため、大規模なテーブルに対してカラム追加を実施する場合に長時間利用できなくなる、という場合もあります。
そのような場合に、グリーン環境というステージング環境を利用することで長時間の停止等を行うことなくカラム追加等のメンテナンスを行うことができる、というのは一つの選択肢として検討できると考えてます。
手動で実装するには大仰ですが、フルマネージドでしたら実装も検討しやすいと思いますので、大規模メンテナンスの場合にもこのフルマネージドのBlue/Greenデプロイを検討してみましょう！


#### おまけ

マニュアルでは「default_transaction_read_only パラメータ (Aurora PostgreSQL の場合) を 1 に設定」してRead Onlyにしてください、とあるので、逆に default_transaction_read_only を 0 に設定して Read Onlyを解除しようとしたのですが、最初はDB上から設定しようとして暫く悩みました。

```SQL: default_transaction_read_onlyパラメータの変更（失敗）
bgtest=> alter database bgtest set default_transaction_read_only = off;
ERROR:  cannot execute ALTER DATABASE in a read-only transaction

bgtest=> alter system set default_transaction_read_only to off;
ERROR:  must be superuser to execute ALTER SYSTEM command
```

#### おまけ２（default値があるカラム追加）

カラム追加時にdefault値をつけると、Green環境のテーブルに値を入れることができます。
今回は testcol 列に test という文字列を default として設定してみました。

```SQL:default値があるカラム追加（切り替え前）
bgtest=> alter table testtbl add column testcol varchar(10) default 'test';
ALTER TABLE
bgtest=> select * from testtbl;
 id |            date            | testcol
----+----------------------------+---------
  1 | 2024-03-19 06:56:34.716652 | test
  2 | 2024-03-19 07:16:01.68611  | test
  3 | 2024-03-19 07:16:02.717927 | test
  4 | 2024-03-19 07:16:03.755583 | test
  5 | 2024-03-19 07:16:04.789024 | test
  6 | 2024-03-19 07:16:05.827665 | test
(6 rows)

bgtest=> \d testtbl
                                  Table "public.testtbl"
 Column  |            Type             | Collation | Nullable |          Default
---------+-----------------------------+-----------+----------+---------------------------
 id      | integer                     |           | not null |
 date    | timestamp without time zone |           |          |
 testcol | character varying(10)       |           |          | 'test'::character varying
Indexes:
    "test_pk" PRIMARY KEY, btree (id)
```

ブルー環境にデータを入れつつ切り替えると、以下のような状態になります。
切り替わるタイミングで id 223 の行にtestcol列が追加されていることが確認できます。

```SQL:default値があるカラム追加（ブルー環境→切替後新しいブルー環境）
INSERT 0 1
WARNING:  there is no transaction in progress
COMMIT
 id  |            date
-----+----------------------------
 223 | 2024-03-19 07:40:54.550932
(1 row)

Tue Mar 19 07:40:54 UTC 2024


ERROR:  cannot execute INSERT in a read-only transaction
WARNING:  there is no transaction in progress
COMMIT
 id  |            date            | testcol
-----+----------------------------+---------
 223 | 2024-03-19 07:40:54.550932 | test
(1 row)

Tue Mar 19 07:41:18 UTC 2024

INSERT 0 1
WARNING:  there is no transaction in progress
COMMIT
 id  |            date            | testcol
-----+----------------------------+---------
 224 | 2024-03-19 07:41:41.879575 | test
(1 row)

Tue Mar 19 07:41:41 UTC 2024
```

#### おまけ３（グリーン環境へのテーブル作成）

グリーン環境に testtbl2 テーブルを作成します。

```SQL:グリーン環境へのテーブル作成
bgtest=> create table testtbl2 ( id integer, date date) ;
CREATE TABLE
bgtest=> \dt
          List of relations
 Schema |   Name   | Type  |  Owner
--------+----------+-------+----------
 public | testtbl  | table | postgres
 public | testtbl2 | table | postgres
(2 rows)

bgtest=> \d testtbl2
              Table "public.testtbl2"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 id     | integer |           |          |
 date   | date    |           |          |
```

特にひねってないのでそのままですが、切替後、新しいBlue環境でも同じようにtesttbl2テーブルが残っていることが確認できます。

```SQL:切替後の新しいBlue環境でのテーブル確認
bgtest=> \dt
          List of relations
 Schema |   Name   | Type  |  Owner
--------+----------+-------+----------
 public | testtbl  | table | postgres
 public | testtbl2 | table | postgres
(2 rows)

bgtest=> \d testtbl2
              Table "public.testtbl2"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 id     | integer |           |          |
 date   | date    |           |          |
```