Cloudflareは、HyperdriveでMySQLサポートの一般提供を開始したと発表した。Hyperdriveは、Workersユーザー向けに、データベース接続をプールし、同社のネットワーク全体で再利用するサービスである。ベータ版から一般提供への移行は、約20件の不具合を修正した後に行われた。その中には、クエリ結果のキャッシュに関するまれな問題も含まれており、データが存在するはずなのに空のレスポンスが返される原因となっていた。
この問題の発生率は約0.001%で、トレースログによる検出は困難だった。Cloudflareは、キャッシュを有効にした場合にのみ不具合が発生することを突き止めた。この問題は、ベータ期間中にMySQLサービスを最も多く利用していた顧客から報告された。
Hyperdriveの仕組み
Hyperdriveは、Cloudflareのネットワーク上でSQL接続をウォームな状態のまま開いて保持し、リクエストごとに新しい接続を作成する代わりに、Workersのリクエスト間で共有する。また、頻繁に実行されるクエリの結果をローカルのCDNキャッシュに保存できるため、レイテンシーとオリジンデータベースへの負荷を軽減できる。
サービスのアーキテクチャは、Workerの近くで動作するClientノードと、接続プールを管理し、オリジンデータベースに近いデータセンターに存在するEndpointノードで構成される。両ノードが同じデータセンターで動作している場合、同じキャッシュを共有することがある。
不具合はMySQLプロトコルの形式の違いに起因
HyperdriveはMySQLを異なる2つの側面から扱う。Workerに対してはサーバーとして動作し、オリジンデータベースに対してはクライアントとして動作する。Workerがネゴシエートするプロトコル機能と、Hyperdriveがデータベースとの間で使用する機能は異なる可能性があるため、サービスは最終クライアントに適合するよう結果メッセージを再構成する必要がある。
調査では、結果セットの終了方法に焦点が当てられた。Node.jsで広く使われているmysql2など、一部のMySQLライブラリはCLIENT_DEPRECATE_EOF機能をサポートしていない。この機能がない場合、サーバーはEOFパケットを使用する。一方、より新しいプロトコルの動作では、結果セットの末尾でEOFパケットの代わりにOKパケットが使用される。
Clientノードは、Workerの機能に合わせて変換した後のバイト列をキャッシュに保存することがあった。一方、Endpointノードは、Hyperdriveの機能に対応した標準形式のデータを想定していた。一方のノードが保存した結果をもう一方のノードが読み取ると、メッセージを適切でない形式として処理する可能性があり、その結果、早期のEOF、ハング、または空の結果が発生していた。
実際に変更された点
Cloudflareは、どのノードが保存したかにかかわらず、すべてのノードがクエリ結果の標準形式のデータをキャッシュに書き込み、常にHyperdriveの機能に従ってエンコードするようにすることで問題を修正した。修正の適用後、空の行が発生する問題は解消され、Cloudflareによると、Client接続の成功率は99.99%に達した。
同社はテスト中に判明したその他の問題も修正した。その一つが絵文字のエンコードに関するエラーである。utf8を使用すると、実際にはutf8mb3が指定されていたため、4バイトのUnicodeコードポイントをすべてサポートできなかった。サービスはutf8mb4へ移行した。また、COM_RESET_CONNECTIONコマンドの実行後にエンコードを再設定するようにした。このコマンドは接続をサーバーのデフォルト設定に戻すため、その設定がlatin1になっている可能性がある。
提供状況と制約
MySQLサポートは本日、Cloudflareのダッシュボードまたはwranglerツールから一般利用できるようになった。PlanetScaleの新しいデータベースへの接続を含め、互換性のあるMySQLデータベースで利用できる。この事例は、中間層での接続プーリングとキャッシュが、SQLの正しさだけに依存するものではなく、プロトコルの状態と各層が想定する形式を正確に一致させる必要があることを示している。