TiProxyのTraffic Replay

## はじめに

TiDB v8.5.0 のリリースとともに、TiProxyのTraffic Replay機能がプレビューになりました。これは下記の記事にあるように、本番環境のトラフィックを開発環境で再現したいといったニーズに対応するツールになります。

https://mita2db.hateblo.jp/entry/2020/08/30/193650

この記事ではTiDB Playgroundを利用して、この機能を試してみます。
1. はじめに、`primary`のデータを`replica`にコピーして、データの状態を揃えます
2. 次に`primary`のTiProxyでトラフィックをキャプチャします
3. 最後に`replica`のTiproxyでそのトラフィックを実行します

## 準備

この記事では、本番を模した `primary` と、開発環境を模した `replica` の２つのTiDB Playgroundを利用します。TiDB Playgroundは通常は起動ごとにデータをクリアしますが、タグをつけることでデータを保存することができます。

### Primary クラスタの準備

まずPrimaryクラスタを起動します。TiProxyを利用するので、`--tiproxy` オプションと、タグを保存する `-T` オプションをつけていることに注意してください。

```bash
> tiup playground v8.5.0 --tiproxy 1 -T primary
Start pd instance: v8.5.0
Start tikv instance: v8.5.0
Start tidb instance: v8.5.0
Start tiproxy instance: v1.3.0
Waiting for tidb and tiproxy instances ready
127.0.0.1:4000 ... Done
127.0.0.1:6000 ... Done
Start tiflash instance: v8.5.0
Waiting for tiflash instances ready
127.0.0.1:3930 ... Done

🎉 TiDB Playground Cluster is started, enjoy!

Connect TiDB:    mysql --host 127.0.0.1 --port 4000 -u root
Connect TiProxy: mysql --host 127.0.0.1 --port 6000 -u root
TiDB Dashboard:  http://127.0.0.1:2379/dashboard
Grafana:         http://127.0.0.1:3000
```

今回は tpcc ベンチをリプレイします。別のコンソールから `tiup bench` を起動し、Primaryクラスタにデータを準備します。

```bash
tiup bench tpcc -H 127.0.0.1 -P 6000 -D tpcc --warehouses 4 prepare
```

データが準備できたら、dumplingでデータを出力しておきます。このデータは `replica` クラスタにimportされます。

```bash
> tiup dumpling -h 127.0.0.1 -P 4000 -u root  \
-B tpcc -o dumpling/ --filetype sql
```

ここまでできたら、Ctrl-CでPrimaryクラスタを落とします。

### Replicaクラスタの準備

Replicaクラスタを起動します。

```bash
tiup playground v8.5.0 --tiproxy 1 -T replica
```

先ほどdumplingでexportしたsqlファイルを実行します。そんなに量が多くないので、シェルスクリプト等で順次実行すれば良いです。こんなスクリプトを作りました。

```sh
#!/bin/bash

# change it to your mysql command
MYSQL_CMD="mysql --host 127.0.0.1 --port 4000 -u root"

# check if directory is provided
if [ -z "$1" ]; then
    echo "Usage: $0 <directory>"
    exit 1
fi

DIR=$1

if [ ! -d "$DIR" ]; then
    echo "Directory $DIR does not exist."
    exit 1
fi

cd "$DIR"

# schema creation
for s in *-schema-create.sql
do
    $MYSQL_CMD < $s
    echo "running $s finished"
done

# table creation and data import
for s in *-schema.sql 
do 
    # get database name from first part of file name
    DB_NAME=$(echo $s | cut -d'.' -f1)
    $MYSQL_CMD -D $DB_NAME < $s
    echo "create db $DB_NAME finished"

    TABLE_NAME=$(echo $s | cut -d'-' -f1)
    echo "importing $TABLE_NAME"

    for r in $TABLE_NAME.*.sql 
    do 
        $MYSQL_CMD -D $DB_NAME < $r
        echo "import $r finished"
        # sleep 3
    done
    echo "import $TABLE_NAME finished"
    $MYSQL_CMD -D $DB_NAME -e 'analyze table '"$TABLE_NAME"';'
done
```

データがインポートできたら、Ctrl-C でreplicaクラスタを落とします。

## トラフィックのキャプチャ

それではトラフィックをキャプチャしていきます。まずはPrimaryクラスタを起動します。

```bash
tiup playground v8.5.0 --tiproxy 1 -T primary
```

TiProxyでデータのキャプチャを開始します。tiupでTiProxyをインストールすると、同時にtiproxyctlコマンドもインストールされますが、パスが通っていません。`tiup --binary tiproxy`でそのパスを表示するのでそれを利用して実行しています。

TiProxyのAPIエンドポイントを指定しますが、デフォルトは3080です。キャプチャ時間を `--duration` で指定します。

```bash
`tiup --binary tiproxy`ctl traffic capture --host 127.0.0.1 --port 3080 --output="$PWD/traffic" --duration=5m
```

:::note warn
outputのパスはフルパスにする必要があります。相対パスにすると、TiProxyのホーム(`~/.tiup/data/tiproxy-0/`) 以下に作成されてしまいます。
:::

キャプチャはバックグラウンドで動きます。すかさずtpccベンチマークを走らせます。
```bash
tiup bench tpcc -H 127.0.0.1 -P 6000 -D tpcc --warehouses 4 --threads 4 --time 3m run
```

キャプチャの状況は `traffic show` で確認できます。
```bash
`tiup --binary tiproxy`ctl traffic show --host 127.0.0.1 --port 3080
```

5分立つとキャプチャが終了しますが、終了通知などはないので、`traffic show` でprogressが100%になったのを確認してください。

trafficディレクトリに、`traffic.log` というファイルができています。これがキャプチャされた実行ログです。

```text
# Time: 2024-12-22T22:27:05.690914+09:00
# Conn_ID: 18
# Cmd_type: StmtPrepare
# Payload_len: 128
SELECT c_discount, c_last, c_credit, w_tax FROM customer, warehouse WHERE w_id = ? AND c_w_id = w_id AND c_d_id = ? AND c_id = ?
...
```

PrimaryをCtrl-Cでシャットダウンします。

## キャプチャのリプレイ

キャプチャをリプレイしてみます。まずは、replicaクラスタの起動です。

```bash
tiup playground v8.5.0 --tiproxy 1 -T replica
```

別のコンソールから、リプレイを実行します。

```bash
`tiup --binary tiproxy`ctl traffic replay --host 127.0.0.1 --port 3080 --input="$PWD/traffic" --username="root"
```

:::note warn
`username` オプションは必須です。このユーザーを使ってリプレイのSQLが実行されます。
:::

リプレイはバックグラウンドで実行されます。状況はまた `traffic show` コマンドで確認できます。

```bash
`tiup --binary tiproxy`ctl traffic show --host 127.0.0.1 --port 3080
[
  {
    "type": "replay",
    "status": "done",
    "start_time": "2024-12-23 11:39:16.270616 +0900 JST m=+157.953784751",
    "end_time": "2024-12-23 11:42:16.286003 +0900 JST m=+337.968426751",
    "input": ".../traffic",
    "username": "root",
    "speed": 1,
    "progress": "100%"
  }
]
```

基本的にリプレイは同じタイミングでSQLを実行します。

### エラーの確認

リプレイのエラーは `tiproxy_traffic_replay` のfailテーブルに保存されています。この例ではエラーが発生しませんでした。

```sql
mysql> SELECT * FROM tiproxy_traffic_replay.fail LIMIT 1\G
Empty set (0.01 sec)
```

## 環境のクリーンアップ

すべて完了したら、環境を削除します。

```bash
tiup clean primary
tiup clean replica
```

## おわりに

TiProxyのTraffic Replay機能について説明しました。この機能はまだプレビューでTiDB Cloudでは利用することができませんが、OSS版/TiUP では利用できますので、ぜひ試してみてください。

https://docs.pingcap.com/tidb/stable/tiproxy-traffic-replay