Hyperdrive の MySQL 対応で見つかった落とし穴: utf8 は utf8mb3、リセット後の文字コード、ケーパビリティ差
Workers から MySQL へつなぐ Hyperdrive の MySQL 対応が GA になり、ベータ中に直したバグが公開された。自前でコネクションプールやプロキシを作る・使うときにも当てはまる MySQL プロトコルの注意点が 3 つある。
どんなときに使う?
- Workers × Hyperdrive で MySQL を使う(または移行を考えている)とき
- ProxySQL などのコネクションプーラーやキャッシュ層を挟んで、絵文字化けや空の結果に悩んでいるとき
落とし穴 1: utf8 は完全な UTF-8 ではない
MySQL の utf8 は utf8mb3 の別名で、1 文字 3 バイトまでしか扱えない。絵文字など 4 バイト文字が壊れる。接続時の文字コードは utf8mb4 を指定する。
落とし穴 2: COM_RESET_CONNECTION で文字コードがサーバー既定に戻る
プールした接続を使い回す前にセッション状態を消す COM_RESET_CONNECTION は、文字コードを「接続時に決めた値」ではなく サーバーの既定値(例: latin1)に戻す。リセットのたびに文字コードを utf8mb4 に設定し直す。
落とし穴 3: クライアントごとにケーパビリティが違う
- MySQL は接続時に、使う機能(ケーパビリティ)をクライアントとサーバーで決める
- Node.js の
mysql2はCLIENT_DEPRECATE_EOFに対応していない。この場合、結果セットの区切りが OK パケットではなく EOF パケットになる - Hyperdrive は内部の固定ケーパビリティでオリジンと話し、Worker 側のドライバーに合わせて応答を書き換えている
- バグの原因は「Worker に返した(書き換え後の)バイト列」をキャッシュしていたこと。同じデータセンターでキャッシュを共有するノード同士で形式が食い違い、約 0.001% の確率で空の結果やハングが起きた
- 修正は、キャッシュには 常にプーラー内部の形式(正規形) で保存し、返すときに変換すること。これで接続の成功率が 99.99% に達した
使い始め方
npx wrangler hyperdrive create HYPERDRIVE_NAME --connection-string="mysql://user:[email protected]:3306/dbname"料金は Postgres 版と同じ。既存のドライバーや ORM がそのまま使える。
注意点
- 1〜3 は Hyperdrive 側で修正済み。ただし自前のプーラーやキャッシュ層を作るときは同じ罠を踏みうる
- テーブル・カラムの文字セットも
utf8mb4になっているかを別途確認する