AlloyDBの無停止インスタンスサイズ変更を試してみた

# はじめに

AlloyDBは2022年に発表されたGoogle Cloud上のPostgreSQL互換のフルマネージドなDBaaSになります。
専用のストレージ・アーキテクチャで構成されるため高いスループットが期待できることや、標準で HTAP (Hybrid Transaction Analytical Processing) が組み込まれていたりすることなど、Google Cloud上で高負荷ワークロードのPostgreSQLを利用したい場合に一番目の選択肢として検討されることになると思います。

AlloyDBの特長の一つに、高可用性が挙げられています。
> AlloyDB は、メンテナンスを含めて稼働時間 99.99% の SLA を提供しています。ほとんどのデータベース障害を、データベースのサイズや負荷に関係なく、60 秒以内に自動的に検出して復旧します。このアーキテクチャでは、無停止でインスタンスのサイズ変更とデータベースのメンテナンスに対応できます。

出典：[AlloyDB for PostgreSQL（主な機能）](https://cloud.google.com/alloydb#key-features)

こちらの「無停止でインスタンスのサイズ変更」について試してみました。

# 「無停止でインスタンスのサイズ変更」の裏側

AlloyDBの無停止インスタンスのサイズ変更については次のような形で実装されているとのことです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/c78cc245-9c67-2669-359a-1a5137da76bd.png)

出典：[A deep dive into AlloyDB for PostgreSQL](https://www.youtube.com/watch?v=iafduXqwfMs)

ちょっとこれだけだと分かり辛いですが、内部的には Cloud SQL Enterprise Plus で行われているように、シャドーレプリカ（上の図だと Replacement Primary）と呼ばれる新しいデータベースが作成され、元のデータベースと完全に同期されたタイミングで切り替わる、というもののようです。

Cloud SQL Enterprise Plus の [ダウンタイムがほぼゼロの計画的メンテナンス](https://cloud.google.com/sql/docs/postgres/maintenance?hl=ja#nearzero) の具体的な実現方法は [「Cloud SQL Enterprise Plusのメンテナンスはどのように実現されているか」](https://zenn.dev/google_cloud_jp/articles/2c35c775ddc7ed) で説明されており、方式としては いわゆる Blue/Green デプロイ が内部的に行われている形になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/202bacc5-42f0-0acf-4ff2-eecde1e723aa.png)

Blue/Greenデプロイについては Amazon Aurora での実装を以前試していますので、興味ある方は次のリンクをご確認ください。

- [Aurora for PostgreSQL の フルマネージド Blue/Green デプロイを試してみた](https://qiita.com/asahide/items/0914ac4132169220ae5f)

また、Azure Database for PostgreSQLでも同様に [「ほぼゼロのダウンタイムのスケーリング」](https://learn.microsoft.com/ja-jp/azure/postgresql/flexible-server/concepts-scaling-resources#near-zero-downtime-scaling) として実装されています。比較的メジャーな実装方法となっていることがわかります。
ただ、Google Cloudの特長としてはいわゆるエンドポイントではなく IPアドレス で接続することができるため、エンドポイントのキャッシュなどの時間も気にすることなく切り替えが行われ、ほかのクラウドだと数十秒の切替時間が必要なところ、１秒未満で切替を行うことができます。

# 「無停止でインスタンスのサイズ変更」を試してみた

次のような形でどのような状況になるのか確認してみます。
1. 2コアのAlloyDBを作成
1. AlloyDBへ1秒毎にデータインサートを行う
1. AlloyDBを4コアにサイズ変更実施
1. AlloyDBの4コアへのサイズ変更完了

ここで 3. と 4. の間でインサートされたデータがどのようになるか、確認してみます。
サイズ変更全体の流れは次のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/6446e6e4-bb47-b8ea-5bcc-6408aa6215f8.png)

#### 1．2コアのAlloyDBを作成

2コアのAlloyDBクラスタを作成します。
![01-alloy.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/ec2ddcb7-19e9-eec1-a21d-e7e502cf6423.png)

#### 2．AlloyDBへ1秒毎にデータインサートを行う

testtblというテーブルを作成し、毎秒データインサートを行います。
注：psqlで接続し、idとcurrent_timestampをインサートし切断する、を毎秒繰り返します
```SQL
postgres=> \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)

postgres=> insert into testtbl values (1, current_timestamp);

INSERT 0 1
WARNING:  there is no transaction in progress
COMMIT
 id |           date
----+---------------------------
  1 | 2024-09-04 08:25:50.202303
(1 row)
```

#### 3．AlloyDBを4コアにサイズ変更実施

alloytest-primaryインスタンスのケバブメニュー（縦の三点）をクリックし、編集→4vCPU を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/6e20a5da-325d-60d4-d966-faef647664ac.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/4c375336-5b5a-2b9f-924b-96442dce968e.png)

変更を行う前に、次のような注意がでます。
「接続に短時間の中断が発生」と記載されてますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/16ab8fa9-e93e-3ccb-d3cb-1143af370ba9.png)

実行するとインスタンスが変更中のステータスになり、また、コンソール下部に進捗が表示されます。
![05-alloy.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/ecb23537-c2f7-07b2-e859-decb4ebb2df2.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/b5dbeece-cc1b-e3b0-52ff-b13abe58889f.png)

#### 4．AlloyDBの4コアへのサイズ変更完了

しばらく待つと更新（サイズ変更）が完了しました。
**17:41:07(GMT+9)**に完了していることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/77d7e99c-d1b5-a6d6-c9dd-cdcc58ea0aca.png)

![08-alloy.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/a8adec02-bb43-e3a4-898d-87aa6e24d588.png)

![09-alloy.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/9515ee89-88b1-d51f-6c43-e9f4bcad6498.png)


データインサートの状況を確認すると、特にエラー等が出ることなく全てのデータインサートが継続されていることが確認できます。（すみません、当日全データにエラーがないことは確認したのですが、その部分のログが手違いで消えてしまいました。。）
```SQL
INSERT 0 1
WARNING:  there is no transaction in progress
COMMIT
 id  |            date            
-----+----------------------------
 921 | 2024-09-04 08:41:05.377775
(1 row)

Wed Sep  4 08:41:05 UTC 2024

INSERT 0 1
WARNING:  there is no transaction in progress
COMMIT
 id  |            date            
-----+----------------------------
 922 | 2024-09-04 08:41:06.423476
(1 row)

Wed Sep  4 08:41:06 UTC 2024

INSERT 0 1
WARNING:  there is no transaction in progress
COMMIT
 id  |           date            
-----+---------------------------
 923 | 2024-09-04 08:41:07.46672
(1 row)

Wed Sep  4 08:41:07 UTC 2024

INSERT 0 1
WARNING:  there is no transaction in progress
COMMIT
 id  |            date            
-----+----------------------------
 924 | 2024-09-04 08:41:08.509606
(1 row)

Wed Sep  4 08:41:08 UTC 2024

INSERT 0 1
WARNING:  there is no transaction in progress
COMMIT
 id  |            date            
-----+----------------------------
 925 | 2024-09-04 08:41:09.553365
(1 row)

Wed Sep  4 08:41:09 UTC 2024

INSERT 0 1
WARNING:  there is no transaction in progress
COMMIT
 id  |            date            
-----+----------------------------
 926 | 2024-09-04 08:41:10.600126
(1 row)
```

# まとめ

ということで、AlloyDBの無停止インスタンスサイズ変更を行うことができました！
めでたしめでたし・・・

と言いたいところですが、通常データベースは毎回接続してクエリを発行するものではなく、データベースに接続したセッション内でクエリを発行する形になります。
元の2コアのデータベースに接続されていたセッションはそのまま引き継がれるのでしょうか？

```SQL
postgres=> select count(*) from testtbl;
 count 
-------
   573
(1 row)

postgres=> select count(*) from testtbl;
SSL SYSCALL error: EOF detected
The connection to the server was lost. Attempting reset: Succeeded.
psql (10.23, server 15.5)
WARNING: psql major version 10, server major version 15.
         Some psql features might not work.
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
postgres=> select count(*) from testtbl;
 count 
-------
   971
(1 row)

postgres=> 
```

当然のことながら、サイズ変更前の2コアに接続されていたセッションで引き続きクエリを実行しようとするとエラーが返ります。ただ、クエリを実行したタイミングで接続のリセットが行われ、自動的に再接続されています。
コネクションプーリングなどを利用する場合には、設定にもよりますが通常は接続がリセットされると再接続されます。そのあたりも含めてシステム全体を設計することで、システムとしては「無停止」を実現することも可能かと考えています。
Amazon AuroraのようなフルマネージドなBlue/Greenが利用できるようになると、さらに使い勝手がよくなると思いますので今後の改善に期待です。