DMSを使ったCloud SQL to AlloyDB移行記

# はじめに

Cloud SQLからAlloyDBへDMSを使って移行をしました。結果的に無停止で移行することができました。
その際の手順や注意点について共有します。

## 移行前の環境

- Cloud Functions, Cloud Run等のアプリケーションからCloud SQLに接続
- アプリケーションはVPCコネクタなしでAuth Proxy経由でCloud SQLのパブリックIPに接続
- 開発者はAuth Proxy経由でCloud SQLのパブリックIPに接続
- RDB（Cloud SQL）はメインのDBではなくそこまで大量のトランザクションはない

# 基本説明

## DMS

[DMS](https://cloud.google.com/database-migration?hl=ja) はDatabase Migration Serviceの略で、Google Cloud（以下GCP）でRDBのデータ移行をするためのサービス。
移行元にAmazon RDSやOracleなども対応している。
今回はCloud SQLからAlloyDBへ移行するために使用した。

## AlloyDB

[AlloyDB](https://cloud.google.com/alloydb?hl=ja) はGCP版のAurora対抗のようなサービス。
Cloud SQLに比べて可用性が高かったり、スケールしやすかったりいい感じになっている。
Cloud SQLと比べてフラグ変更などでダウンタイムが発生しないのがとりあえず1番うれしい。
（max_connectionsの変更など再起動がかかるものもある）

# 移行手順

## 1. 事前準備

### 1-1. Cloud SQLをプライベートIP接続に切り替える

RDSとか使ってるとそんなものという感じがするが、Cloud SQLではデフォルトでパブリックIPが有効＆プライベートIPなしで作成されて、 [Cloud SQL Auth Proxy](https://cloud.google.com/sql/docs/postgres/sql-proxy?hl=ja) を使ってトンネルして接続する。

現状、AlloyDBは逆にプライベートIPしか使えない素朴な仕様なのでCloud SQLでパブリックIP接続を使っている場合は接続環境を変える必要がある。

Cloud SQLからAlloyDBへの差分を小さくするために、まずCloud SQLにプライベートIPを使って接続するようにVPCネットワークの整備、アプリケーションの接続設定などを整えた。

### 1-2. Cloud SQLのレプリケーション用の設定

DMSを作成する際のフローの中で注意書きが表示されるが、pglogicalを使って移行元のDBに対してレプリケーションができる状態をセットアップする必要がある。

具体的には以下を行う。

1. Cloud SQLのフラグに `logical_decoding`, `enable_pglogical` をonで追加
1. DMSに使用するユーザーの作成（既存のユーザーを使うならそれでも）
1. 以下のクエリ実行
    databaseごと
    ```sql
    CREATE EXTENSION IF NOT EXISTS pglogical;
    GRANT USAGE on SCHEMA pglogical to <user>;
    GRANT SELECT on ALL TABLES in SCHEMA pglogical to <user>;
    GRANT SELECT on ALL SEQUENCES in SCHEMA pglogical to <user>;
    ALTER USER dms with REPLICATION;
    ```

    schemaごと
    ```sql
    GRANT USAGE on SCHEMA <schema> to <user>;
    GRANT SELECT on ALL TABLES in SCHEMA <schema> to <user>;
    GRANT SELECT on ALL SEQUENCES in SCHEMA <schema> to <user>;
    ```

### 1-3. テーブルのスキーマ変更を凍結する（方が楽）

テーブルのスキーマ変更やテーブルの追加・削除は移行しきるまで凍結した方が楽。
そうでない場合はいちいちレプリケーション用に反映する作業をしないといけない。

チームで合意を取って一定期間の凍結を実施した。

## 2. DMSを立てる

DMSは現状Terraform対応していないので手で立てていった。 
以下にissueは立っている。

https://github.com/hashicorp/Terraform-provider-google/issues/9624

また、DMSでは既存のAlloyDBクラスタを選択するようなことはできず、AlloyDBはDMS管理のもとで一緒に作成される形となるため、AlloyDBについてもTerraform化はできない。

（最終的にDMS管理下から外れたら `terraform import` をすることは可能なはず。まだできていない。）

DMSの作成については基本的にフォームの案内に従っていけばOK。
以下、接続プロファイルについての注意点。

* 油断すると接続プロファイルの作成フォームでCloud SQLのIPが自動でパブリックIPで入力されるが、たぶんプライベートIPにしないといけない（もしかしたら自動でAuth Proxyが使われていけるのかも）
* "暗号化のタイプ" は作成時点だとフォームで設定しても反映されないバグがあった。サポートに問い合わせたところ修正予定ということだったので今は直っているかも。CLIで作成すればいける。
* "接続方法の定義" は説明に「Oracle と Cloud SQL for PostgreSQL の間で移行するための接続プロファイルを使用する予定がある場合」とあるように、未定義のままにする。よく読まずに"パブリックIP"とか"プライベートIP"とかを選択しないようにする。

以下、AlloyDBについての注意点

* フラグ指定をここでやっておかないと、DMSの管理から外すまで設定できなくなる
* インスタンスサイズも同様に、DMS管理下では設定が変更できない

### 2-1. SEQUENCEの負荷が高いとdead lockで開始できない問題

id発番等にSEQUENCEを使っていて、ある程度の負荷があるとdead lockでDMSが開始できない問題が発生した。
たまたまプロダクトでのSEQUENCEを使ったテーブルに対する書き込みがPub/Subを使った非同期のものだけだったので、Subscriptionを一時的に滞留させてなんとかなったが、けっこうこの制限を回避できないケースは多そう。

:::note info
pglogical、DMSどちらの性質なのかわかっていないですが、もし詳しい方いたら回避策など知りたいです
:::

## 3. AlloyDBの整備

### 3-1. 開発者用のプロキシを立てる

Cloud SQLのようにパブリックIPで接続ができないので開発者がさわるために何かしらの整備が必要。
今回はGCEで [alloydb-auth-proxy](https://github.com/GoogleCloudPlatform/alloydb-auth-proxy) をコンテナ起動する形にした。

### 3-2. ユーザー作成


Cloud SQLではGCPリソースとしてユーザーを管理することができたが、AlloyDBでは現状そういった機能はないので、PostgreSQLとしてのユーザーを素朴に作る形になる。

Cloud SQLで使っていたユーザーを適切な権限で作成する。

## 4. AlloyDBに切り替える

DMSによる同期が一定落ち着いてきたタイミングで、アプリケーションをsecret更新等でAlloyDBに向ける。
切り替えが全体で完了するまではAlloyDBにしか存在しないデータを参照しようとして問題になりうるが、プロダクトのワークロードとしてそこまで結合の強いものはなかったので最終的にAlloyDBにはすべてのデータが揃っていればOKと判断して無停止で移行した。

## 5. DMSでプロモートを実行

プロモートを実行するとAlloyDBがDMS管理から外れて独立した状態になる。

## 6. テーブルのオーナーを変更

DMSを使った状態でAlloyDBのテーブルのオーナーが `alloydbexternalsync` というユーザーになっている。
この状態だとアプリケーションで使用するユーザーでテーブルのスキーマ変更等ができないのでオーナーを変更する。

## 7. 事後処理

Cloud SQLの停止、削除などを実施していく。


# おわりに

フォームにバグがあったり、SEQUENCEのdead lockの問題があったりで何度かつまづきましたが、ある程度マネージドされた楽な手順で移行を完了させることができました。
インスタンス（クラスタ）ごと移行しないといけなかったり、dead lockの問題だったりで、世の中的にもチーム内の他のプロダクト的にもDMSを採用できるユースケースは一定限られるかもしれませんが、そのジャッジや採用した際の参考になれば幸いです。

今後はしばらく運用しながらAlloyDBの特性を活かしていい感じにしていき、また知見があれば共有したいと思います。