AWS DMSを活用してHasura CloudとMySQLを連携する

## はじめに
[公募ガイド社](https://www.koubo.co.jp/company/)という出版社でエンジニアをやっている@ueda000です。
現在システム大改造/新規サービス計画中につき、エンジニア大募集してます！(特にフロントエンド！)

## Hasura CloudでもMySQLが使いたい
2022年2月21日現在、Hasura CloudとMySQLは連携することができません(CloudじゃなくてHasuraのDockerイメージ使うなら[Previewで利用可能](https://hasura.io/docs/latest/graphql/core/guides/mysql-preview.html))。
そこで、AWS Database Migration Service (AWS DMS)を使ってMySQLをPostgreSQLに常時レプリケーションすることで、Hasura CloudでMySQLを扱えるようにしてみます。
まあなので結局のところ、単に「AWS DMS使ってMySQLをPostgreSQLにレプリケーションしてみた」という話です。

## 今回やること
![hasura_awsdms.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1994570/57c13599-cc3e-0349-c1a1-7065a8f13ec0.png)

今回は既にあるAmazon RDS for MySQLをHasuraと繋げたかったので、新たにレプリケーション用のPostgreSQLを作るところから始めます。
AWS外のDBにレプリケーションできるか試したかったので、GCPのCloud SQL for PostgreSQLを使ってみることにします。別にGCPじゃなくてもいいですし、AWS同士でももちろんOKです。
あと、別にMySQL側がAWSじゃなくてもAWS DMSは使えます。オンプレとかでもいけるはず。

また、Hasuraからはとりあえず参照だけできればよかったので、MySQL → PostgreSQL の単方向レプリケーションのみをやります。
今回はやってないですが、双方向レプリケーションも多分できると思います([後述](#双方向レプリケーション))。

## PostgreSQLのDBを用意する
前述の通り、今回はGCPのCloud SQLを使いました。
AWS DMSとつなげるためにパブリックIPを割り当てます。承認済みネットワークの追加はこのあとやるので飛ばします。

## AWS DMSのもろもろを作る
これ↓を参考にやっていきます。

https://www.workfall.com/learning/blog/how-to-migrate-rds-mysql-database-to-rds-postgresql-database-using-aws-database-migration-servicedms/

#### レプリケーションインスタンスを作る
移行タスクを実行してくれるインスタンスのことです。
今回はPostgreSQL側がGCPにあるので、「パブリックアクセス可能」にします。また、MySQL側と接続できるようセキュリティグループを適宜設定しておきます。
インスタンスを作成したら、パブリックIPをPostgreSQL側(Cloud SQL)のネットワークに追加して、アクセスできるようにします。


#### ソースエンドポイントを作る
今回だとソース=MySQL側のことです。Amazon RDSなので、ドロップダウンから対象のRDSを選択すれば接続先の入力が省けて楽です。

ソースとしてMySQLを選択すると、

>ソースデータベースは MySQL です。進行中の変更のレプリケートには、行に対して MySQL バイナリログを有効にし、設定する必要があります。
バイナリログが十分な期間 (通常は 24 時間) サーバーで保持されていることを確認してください。 RDS インスタンスでのバイナリログの保持期間を設定するには、このコマンドを使用できます。call mysql.rds_set_configuration(''binlog retention hours'', 24);

という警告が出てきました。
MySQLバイナリログの設定を確認するために以下コマンドを実行します。

```SQL
call mysql.rds_show_configuration;
```

値がNULLになってたので、警告内のコマンドを実行して24時間に設定しておきます。

```SQL
call mysql.rds_set_configuration('binlog retention hours', 24);
```

エンドポイントを作成したら、接続テストができるので、実施して成功することを確認します。

#### ターゲットエンドポイントを作る
PostgreSQL側です。Cloud SQLの接続情報を入力していきます。サーバ名のところはCloud SQLのパブリックIPを入れます。
こちらも作成後は接続テストを実施しておきます。

#### 移行タスクを作る
移行タイプは「既存のデータを移行して、継続的な変更をレプリケートする」にします。
テーブルマッピングについては

>include アクションを含む選択ルールを 1 つ以上指定する必要があります。 

とのことなので、とりあえず適当なテーブルを1つ指定した「含む(Include)」アクションの選択ルールを作ってみます。
全スキーマ/全テーブルをレプリケーションしたい方は、スキーマ名/テーブル名ともに`%`にしてアクションは「含む(Include)」にすればOKだと思います。

## AWS DMSの移行タスクを実行する
作成した移行タスクを実行すると、以下のエラーとなりました。

>MySQL binary Logging must use ROW format; Errors in MySQL server binary logging configuration. Follow all prerequisites for 'MySQL as a source in DMS' from https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Source.MySQL.html ...

このリンク先には、ソースにMySQL使う場合の諸注意がいっぱい書いてあります。本番運用するときはちゃんと確認したほうがよさそうです。
今回はロクに読まないでとりあえずエラーメッセージで怒られてる内容だけ直していきます。RDS(MySQL)のパラメータグループを開いて、パラメータ`binlog_format`の値を`ROW`に変更します。

設定後、改めて移行タスクを実行し、成功しました。データが移行されていること、MySQL側の更新が反映されることを確認しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1994570/228d6a05-aa76-d38a-d97b-12814e98d465.png)

## Hasura CloudとPostgreSQLを繋ぐ
本当はAPI認証の方が推奨されてるのですが、今回はHasura CloudのパブリックIPをCloud SQL(PostgreSQL)にネットワーク追加してアクセス可能にします。

Hasura Cloudのコンソールから接続設定して完了です。


## 双方向レプリケーション
今回はMySQL→PostgreSQLの単方向レプリケーションでしたが、

- MySQL→PostgreSQL
- PostgreSQL→MySQL

の2つの移行タスクを作れば双方向レプリケーションもできるみたいです。

https://docs.aws.amazon.com/ja_jp/dms/latest/userguide/CHAP_Task.CDC.html#CHAP_Task.CDC.Bidirectional

## おわりに
まあしばらくすればHasura Cloudが公式にMySQLをサポートすると思うのですが、それまで待てない！という方はそれまでの繋ぎとして検討してみてもいいんじゃないでしょうか。
ただ、PostgreSQL側のDBインスタンスのコストが余分にかかることになるので、レプリケーションするデータ量が結構多いなら要注意です。

あと、AWS DMSの代わりに、GCPにも同様のサービスあるのでこれを使っても同じことできそうですね。

https://cloud.google.com/database-migration