ダウンタイムなしでCassandraを移行する方法: ZDM Proxyを使ったステップバイステップガイド

## はじめに

データベースの移行は技術的なチャレンジであり、特にゼロダウンタイムを達成することはさらに難易度が高いです。この記事では、Cassandra Zero Downtime Migration (ZDM) Proxy を使用して、ダウンタイムなしで Cassandra データベースを新しいクラスタに移行する方法を解説します。

## ZDM Proxy とは？

ZDM Proxyは、Apache CassandraコミュニティをPMC(Project Management Committee)として主導しているDataStax社が提供する、Apache Cassandra クラスタ間の移行をサポートするオープンソースの中間層コンポーネントです。このプロキシは、接続先以外にアプリケーションクライアントのコード変更を必要とせず、プロキシがオリジナルクラスタ（Origin）とターゲットクラスタ（Target）の両方に接続し、Origin から Target への移行をゼロダウンタイムで実現します。

## メリットとデメリット
### メリット

- ゼロダウンタイム: ZDM Proxyを使用することで、サービスの中断を避けながらデータベースの移行を行うことができます。
- 最小限のアプリケーション変更: クライアントアプリケーションに対する変更は、プロキシへの接続情報の変更のみです。ただし、uuid() 関数はサポートされていないため、アプリケーション側での変更が必要です。また、now() 関数もデフォルトではサポートされていない点にご注意ください。
- 柔軟性: CQLをサポートするさまざまなデータベース構成に適応可能です。

### デメリット

- レイテンシ増加: プロキシを介することで、わずかながらレイテンシが増加する可能性あります。

## 移行プロセス
### 概要

このセクションでは、データベースのゼロダウンタイム移行（ZDM）プロセスの概要を説明します。

- 事前準備：旧クラスタ（Origin Cluster）と新クラスタ（Target Cluster）に、同一のキースペースとテーブルを準備します。
- ZDM Proxyの設定：必要な環境変数を設定した後、ZDM Proxyを起動します。これにより、アプリケーションはプロキシ経由でデータベースにアクセスするようになります。
- データの移行と検証：既存のデータを旧クラスタから新クラスタへ移行し、データが正しく複製されていることを確認します。
- 読み取りモードの変更（オプション）：必要に応じて、読み取りを旧クラスタと新クラスタの両方から行う設定に変更することができます。
- プライマリクラスタの切り替え：すべてのアプリケーションがZDM Proxyを介して新クラスタに接続できるようになったら、プライマリクラスタを新クラスタに切り替えます。
- 直接接続への移行：最終的に、アプリケーションが直接新クラスタに接続するように設定を変更します。

### 事前準備

旧クラスタ（Origin Cluster）と新クラスタ（Target Cluster）に同一のキースペースとテーブルを準備します。

Originクラスタの準備
```
cqlsh host1
CREATE KEYSPACE ks  WITH REPLICATION = {     'class' : 'SimpleStrategy',     'replication_factor' : 1    };
CREATE TABLE tb ( id int PRIMARY KEY,  lastname text, firstname text );
INSERT INTO tb (id, lastname, firstname) VALUES (1, 'a', 'a');
INSERT INTO tb (id, lastname, firstname) VALUES (2, 'a', 'a');
INSERT INTO tb (id, lastname, firstname) VALUES (3, 'a', 'a');
```

Targetクラスタの準備
```
cqlsh host2
CREATE KEYSPACE ks  WITH REPLICATION = {     'class' : 'SimpleStrategy',     'replication_factor' : 1    };
CREATE TABLE tb ( id int PRIMARY KEY,  lastname text, firstname text );
```

### ZDM Proxyの設定

![stp1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3723531/fe1e8bde-7396-8986-01db-9d7b01a89f27.png)

ZDM Proxyを設定し、アプリケーションがプロキシ経由でデータベースにアクセスするようにします。まず、以下の環境変数を設定して、OriginクラスタとTargetクラスタの接続情報を指定します。

```
export ZDM_ORIGIN_CONTACT_POINTS=host1
export ZDM_ORIGIN_USERNAME=user1
export ZDM_ORIGIN_PASSWORD=password
export ZDM_ORIGIN_PORT=9042

export ZDM_TARGET_CONTACT_POINTS=host2
export ZDM_TARGET_USERNAME=user2
export ZDM_TARGET_PASSWORD=password
export ZDM_TARGET_PORT=9042

export ZDM_PROXY_LISTEN_PORT=14002
export ZDM_PROXY_LISTEN_ADDRESS=127.0.0.1
export ZDM_PRIMARY_CLUSTER=ORIGIN
export ZDM_READ_MODE=PRIMARY_ONLY
```

ZDM ProxyはGo言語で実装されているため、Goがインストールされている環境で以下のコマンドを実行してコンパイルし、起動します。
```
git clone https://github.com/datastax/zdm-proxy.git
cd zdm-proxy/proxy
go build -o zdm-proxy
./zdm-proxy
```

デモの目的で、Cassandraの標準CLIツールであるcqlshを使ってZDM Proxyに接続します。通常、クライアントアプリケーションではCassandraドライバを通じて接続しますが、ここでは簡易的にcqlshを使用します。

```
cqlsh 127.0.0.1 14002 -u $ZDM_ORIGIN_USERNAME -p $ZDM_ORIGIN_PASSWORD
```
この接続を通じて、Origin側のデータを確認することができます。
```
select * from ks.tb;
 id | firstname | lastname
----+-----------+----------
  1 |         a |        a
  2 |         a |        a
  3 |         a |        a
(3 rows)
```

ZDM Proxyを介してデータを追加後、OriginおよびTargetクラスタでデータが正しく追加されたことを確認します。
```
INSERT INTO ks.tb (id, lastname, firstname) VALUES (4, 'b', 'b');
INSERT INTO ks.tb (id, lastname, firstname) VALUES (5, 'b', 'b');
select * from ks.tb;
 id | firstname | lastname
----+-----------+----------
  5 |         b |        b
  1 |         a |        a
  2 |         a |        a
  4 |         b |        b
  3 |         a |        a
```

Originクラスタ側で新規追加されたデータを確認できます。
```
cqlsh host1 -u $ZDM_ORIGIN_USERNAME -p $ZDM_ORIGIN_PASSWORD
select * from ks.tb;
 id | firstname | lastname
----+-----------+----------
  5 |         b |        b
  1 |         a |        a
  2 |         a |        a
  4 |         b |        b
  3 |         a |        a
```

Targetクラスタ側でも新規追加されたデータを確認できます。
```
cqlsh host2 -u $ZDM_TARGET_USERNAME -p $ZDM_TARGET_PASSWORD
select * from ks.tb;
 id | lastname | firstname
----+----------+-----------
  4 |        b |         b
  5 |        b |         b
```

### データの移行と検証

![stp2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3723531/5aef2469-ccd9-4f39-8ec2-72e0970a170e.png)

データ移行プロセスにはいくつかのアプローチがありますが、ここではZDM Proxyを使用した場合の簡易的なデータ移行方法を紹介します。この方法は、開発やテスト環境での使用を想定しています。

最初に、Originクラスタからデータをエクスポートします。以下のCOPYコマンドを使用して、ks.tbテーブルのデータをCSVファイルに出力します。このコマンドは、cqlshを通じて実行します。
```
cqlsh host1 -u $ZDM_ORIGIN_USERNAME -p $ZDM_ORIGIN_PASSWORD
COPY ks.tb TO 'tb.csv' WITH HEADER=TRUE;
```

次に、エクスポートしたデータをTargetクラスタにインポートします。同じくCOPYコマンドを使用して、CSVファイルからデータをks.tbテーブルに取り込みます。
```
cqlsh host2 -u $ZDM_TARGET_USERNAME -p $ZDM_TARGET_PASSWORD
cqlsh> COPY ks.tb FROM './tb.csv' WITH HEADER=TRUE ;
```

注意：この簡易的なCOPYコマンドを用いた方法は、開発やテスト環境での使用に限定してください。本番環境では、データの一貫性、パフォーマンスへの影響、および大量データの取り扱いに関するリスクを考慮し、より堅牢なデータ移行ツールの使用を推奨します。

DataStaxのCassandra Data Migratorは、本番環境での使用に適した代替手段です。このツールは、大規模なデータセットの効率的な移行を支援し、データの一貫性を保つための高度な機能を提供します。

### 読み取りモードの変更

![stp3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3723531/6e08a89f-d735-7cc2-4642-8f357c2eaa67.png)

この手順は、Targetクラスタのパフォーマンスを評価し、新しい設定がアプリケーションの応答性とデータ処理能力にどのように影響するかを確認するために重要です。読み取りモードをDUAL_ASYNC_ON_SECONDARYに変更することで、読み取り要求がTargetクラスタに非同期で送信されるようになり、OriginクラスタとTargetクラスタのパフォーマンスを比較することが可能になります。

以下の環境変数を設定することで、ZDM Proxyの読み取りモードを変更します。
```
export ZDM_READ_MODE=DUAL_ASYNC_ON_SECONDARY
```

読み取りモードの変更を有効にするためには、ZDM Proxyを再起動する必要があります。プロキシを停止し、再起動します。本番環境では複数のZDM Proxyインスタンスを順次に再起動してください。

DUAL_ASYNC_ON_SECONDARYモードが有効になると、読み取りリクエストはオリジナルクラスタとターゲットクラスタの両方に送信されますが、レスポンスはターゲットクラスタからのみ返されます。これにより、ターゲットクラスタの応答時間と処理能力を、オリジナルクラスタと比較して評価することができます。この評価を通じて、ターゲットクラスタが本番環境への移行に適しているかどうかを判断するための重要なデータが得られます。


### プライマリクラスタの切り替え

![stp4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3723531/9f05caac-ef97-a3b0-35aa-100e0e042c2c.png)

すべてのアプリケーションがZDM Proxyを介して新クラスタに接続できる状態になったら、プライマリクラスタを新クラスタに切り替える時が来ました。この操作を行うことで、新クラスタが全ての読み取りおよび書き込みリクエストの処理を担うようになります。

ZDM Proxyの環境変数を更新して、新クラスタをプライマリとして設定します。
```
export ZDM_READ_MODE=PRIMARY_ONLY
export ZDM_PRIMARY_CLUSTER=TARGET
```
ここで、ZDM_READ_MODE=PRIMARY_ONLYは全ての読み取りリクエストがプライマリクラスタ（この場合は新クラスタ）からのみ行われるように設定します。ZDM_PRIMARY_CLUSTER=TARGETは新クラスタをプライマリクラスタとして指定します。

設定を適用するために、ZDM Proxyを再起動します。

この切り替えにより、新クラスタが全てのデータベースリクエスト（読み取りおよび書き込み）の処理を担当します。この変更は透過的であり、アプリケーションコードの変更を必要とせず、ZDM Proxyを通じて自動的に処理されます。切り替え後、新クラスタのパフォーマンスと安定性を継続的に監視し、問題がないことを確認してください。

### 直接接続への移行

移行プロセスの最終段階では、アプリケーションがZDM Proxyを介さずに、直接Targetクラスタにデータリクエストを送信するように設定を変更します。このステップにより、移行プロセスが完了し、新クラスタがプライマリデータベースとして機能し始めます。

## 最後に

この記事を通じて、Apache CassandraのZero Downtime Migration (ZDM) Proxyの概要、その利点、そして具体的な移行プロセスを詳しく解説しました。また、ZDM Proxyに関するさらなる情報は、[公式ドキュメント](https://docs.datastax.com/en/astra-serverless/docs/migrate/introduction.html)を参照してください。