🌊

APIもDBも東京なのに、全クエリが太平洋横断していた話

に公開1

はじめに

AI x 旅行アプリのアバトラベル(iOS / Android)は、2026年6月にフルリニューアルしました!
ところがその直後から、「ほぼ全てのAPIが一律に遅い」 という不思議な症状に悩まされていました。弊社に協力いただいているつよつよエンジニアの栗田さんの調査により、原因はAPIのコードでもDBのチューニング不足でもなく、接続先ホスト名のDNS解決にありました。

  • バックエンドAPI:FastAPI(Python)、Fly.io東京リージョン(nrt)で稼働
  • DB:PlanetScale(AWS ap-northeast-1 = 東京)

という同一リージョン構成にもかかわらず、全クエリが太平洋を渡って米オハイオへ往復していたのです。

環境変数を1つ変えるだけでSELECTが約35倍速くなった一方、原因にたどり着くまでの切り分けにはそれなりの回り道がありました。同じ構成(ジオDNSで接続先が決まるマネージドサービス × PaaS)なら他社でも普通に起こり得る話なので、事例として共有します。

TL;DR

  • PlanetScaleの接続ホストaws.connect.psdb.cloudoptimizedホストで、Route 53のレイテンシベースルーティングにより、返すIPを「APIサーバーの位置」ではなく 「問い合わせ元DNSリゾルバの位置」 で決める
  • Fly.io東京(nrt)のマシンからこのホスト名を解決すると、米オハイオ(us-east-2)のIPが返っていた
  • DB本体は東京(ap-northeast-1)にあるため、「同一リージョンのはずなのに全APIが遅い」という症状になっていた
  • リージョン固定のdirectホストap-northeast.connect.psdb.cloudに変更したところ、SELECT 1クエリが294ms → 8ms(約35倍) に改善した

症状:ほぼ全てのAPIが一律に遅い

もともと200〜600ms程度で返っていたAPIが、軒並み1秒超になりました。ポイントは次の2つです。

  • 特定のエンドポイントではなく、DBを触るほぼ全てのAPIが遅い
  • 遅くなり方が「一律」で、どのAPIも同じくらい底上げされている

厄介だったのは遅くなりだしたのがフルリニューアルの時期と被っていたことです。当初は「新機能のコードが何か悪さをしているのではないか」と疑い、社内では「非同期処理の実装が不適切で、どこかでイベントループを塞いでいるのではないか」という議論もありました。FastAPIの非同期アーキテクチャでは1箇所のブロッキング処理が全APIを巻き込んで遅くするため、「全APIが一律に遅い」という症状とも矛盾しない、それらしい仮説です。

結果的にこれらは完全なミスリードで、原因はコードとは無関係でした。「直近のリリースと症状の時期が重なっている」ことは、因果の証拠にはならない——後から振り返ると、これが最初の教訓です。

切り分け:「1クエリの往復コスト」だけが異常に高い

本番相当の検証用APIサーバー(Fly.io nrt / 本番と同一DB)を立て、リクエスト内の区間ごとに処理時間を計測しました。計測対象は、ログインユーザー自身の情報を返すだけの軽量なエンドポイント(GET /me)です。

区間 実測
Firebase認証(verify_id_token 約2ms
SELECT(1クエリ) 294ms
COMMIT 582ms
GET /meのサーバー内総時間 1,038ms

認証やCPUは無関係(verify_id_tokenは終始~2msで変化なし)で、「DBとの1往復」だけが異常に高いことが分かります。もしイベントループがブロックされているなら、DBと無関係な認証処理も一緒に遅くなるはずで、ここで「非同期処理の実装が不適切」説は棄却されました。そして、東京のAPIサーバーから東京のDBへの1クエリに294msはあり得ない数字です。ここでようやく、コードではなくネットワーク経路を疑い始めました。

根本原因:ジオDNSが「東京のFly」に「オハイオのIP」を返していた

PlanetScaleの「optimizedホスト」の仕組み

PlanetScaleの接続ホスト名には2種類あります。

種類 接続先の決まり方
optimizedホスト aws.connect.psdb.cloud Route 53のレイテンシベースルーティングで最寄りのエッジに接続
directホスト ap-northeast.connect.psdb.cloud リージョン固定

公式ドキュメントは、ほとんどのケースでoptimizedホストを推奨しています。通常はこれで正しく最寄りのエッジに繋がりますし、リージョン単位のネットワーク障害を迂回できる利点もあります。

問題はRoute 53のレイテンシベースルーティングが「接続元に最も近いリージョン」をどう推定するかです。EDNS Client Subnetが渡らない場合、DNS問い合わせをしてきたリゾルバの送信元IPから位置を推定します。つまり、APIサーバーが使うDNSリゾルバの「見かけの位置」がサーバーの実際の位置とズレていると、最適化のつもりで最悪のIPが返ることがあります。

Fly.io東京からの実測

Fly.io nrt上のマシンと、東京のローカル端末のそれぞれでaws.connect.psdb.cloudを解決した結果です。

解決元 解決先IP リージョン
Fly.io nrt(本番と同じ) 3.13.97.145 / 3.15.73.63 / 3.135.144.115 us-east-2(米オハイオ)
ローカル(東京) 3.114.31.95 / 18.176.161.148 / 52.197.115.44 ap-northeast-1(東京)

Fly.io側の名前解決は、fly ssh consoleで本番マシンに入って確認できます。
python:slimのような軽量イメージにはdighostが入っていないことが多いですが、glibc付属のgetentならそのまま使えます)

$ fly ssh console -a <your-app> -C "getent hosts aws.connect.psdb.cloud"
3.135.144.115   aws.connect.psdb.cloud
3.13.97.145     aws.connect.psdb.cloud
3.15.73.63      aws.connect.psdb.cloud

解決先IPが本当に北米なのかを確定させる

「3.13.97.145」という数字を見ても、それがどこにあるのかは分かりません。IPから場所を確定させる方法はいくつかあります。

一番手軽なのは逆引きです。AWSのEC2系IPは、逆引きホスト名にリージョン名がそのまま入っています。

$ host 3.13.97.145
145.97.13.3.in-addr.arpa domain name pointer ec2-3-13-97-145.us-east-2.compute.amazonaws.com.
# → us-east-2(米オハイオ)

$ host 3.114.31.95
95.31.114.3.in-addr.arpa domain name pointer ec2-3-114-31-95.ap-northeast-1.compute.amazonaws.com.
# → ap-northeast-1(東京)

地理情報として見たい場合は、ipinfo.ioなどのlookupサービスに投げると都市名まで返ってきます。

$ curl -s https://ipinfo.io/3.13.97.145/json
{
  "ip": "3.13.97.145",
  "hostname": "ec2-3-13-97-145.us-east-2.compute.amazonaws.com",
  "city": "Columbus",
  "region": "Ohio",
  "country": "US",
  ...
}

厳密に確定させたい場合は、AWS公式ip-ranges.jsonと照合します。今回も最終的にはこれでus-east-2であることを確認しました。

「DNSの一点だけが問題」という構図

さらに、Fly.io nrtから東京リージョンのIPへ直接TCP接続すると2.6msでした。つまり、

  • ネットワークは速い
  • 宛先(東京のPlanetScale)も速い
  • DNSが東京のFlyにオハイオのIPを返している、その一点だけが問題

という構図です。

本来:  API(東京) ⇄ PlanetScale(東京)        … 数ms
実際:  API(東京) ⇄ エッジ(米オハイオ) ⇄ …    … 数百ms

手元では再現しないのか?

この問題の性質が悪いところは、手元の端末からはまったく再現しない点です。東京のローカル端末からpscale pingを実行すると、こう見えます(出力は全エンドポイント21行あるため、関係する3行を抜粋)。

リージョン レイテンシ エンドポイント 種類
AWS ap-northeast-1 35.2ms aws.connect.psdb.cloud optimized
AWS ap-northeast-1 45.4ms ap-northeast.connect.psdb.cloud direct
AWS us-east-2 179.6ms aws-us-east-2.connect.psdb.cloud direct

手元のリゾルバは東京にいるので、optimizedホストは正しく東京のIPに解決され、それどころかdirectホストより速いくらいです。「手元で計測して問題なし」は、本番のリゾルバ環境について何も証明していません。 レイテンシやDNSの検証は、本番と同じネットワークから(Fly.ioならfly ssh consoleで本番マシンに入って)行う必要があります。

修正:directホストへの1行変更

修正は環境変数1つです。ジオルーティングするoptimizedホストから、東京リージョン固定のdirectホストへ変更します。

flyctl secrets set DATABASE_HOST=ap-northeast.connect.psdb.cloud -a <your-app>

directホストはリージョン固定なので、リゾルバがどこにいようとオハイオに飛ばされることはありません。

PlanetScale公式も、directホストを使うべきケースを「アプリケーションとDBが同一リージョンにあり、optimizedホストが最適な経路を取っていないと確認できた場合」と定義しており、今回はまさにその誤ルーティングを実測で確認したケースに該当します。

before / after

同一DB・同一構成の検証用APIサーバーでの実測です。

項目 before aws.connect.psdb.cloud(→オハイオ) after ap-northeast.connect.psdb.cloud(→東京固定) 改善
SELECT(1クエリ) 294ms 8〜9.7ms 約35倍
COMMIT 582ms 9.9〜11.4ms 約53倍
GET /meサーバー内総時間 1,038ms 28〜36ms 約36倍

コード変更ゼロ、環境変数1つでこの結果です。1リクエストが複数クエリを発行するAPIほど「往復回数×1往復のコスト」で被害が増幅されるため、全APIが一律に底上げされていた症状とも整合します。

実際に本番へ適用したところ、Sentryで見ていたAPI全体のp75レスポンスタイムも、適用を境に一気に元の水準へ落ちました。

本番適用を境にp75レスポンスタイムが急落しているSentryのグラフ

同じ轍を踏まないためのチェックリスト

今回の学びを一般化すると、次のようになります。

  • 「全APIが一律に遅い」ときは、個々のコードより先に「1往復の単価」を疑う。 N+1のような「往復回数」の問題と、今回のような「1往復のコスト」の問題は独立している。後者はコードをいくら読んでも見つからない
  • レイテンシ・DNSの検証は本番と同じネットワークから行う。 手元のdigpscale pingは、手元のリゾルバに対する答えでしかない
  • ジオDNS(レイテンシ/位置ベースルーティング)のエンドポイントは「リゾルバの位置」で解決されることを知っておく。APIサーバーの位置とリゾルバの見かけの位置がズレる環境(PaaSは特に)では、最適化が逆向きに働き得る
  • 解決先IPのリージョンは推測せず確定させる。 本番マシンで名前解決してIPを取り(digが無いコンテナでもgetent hostsが使える)、逆引き(host)・ipinfo.io・AWSのip-ranges.jsonなどと照合すれば数分で白黒つく
  • 「直近のリリースと時期が重なった」は因果の証拠にならない。 切り分け(今回なら認証~2ms不変、TCP直結2.6ms)で外堀を埋めてから原因を確定する

PlanetScale + Fly.ioに限った話ではない

Fly.ioのコミュニティフォーラムにも同型の事例があります。ロンドン(lhr)のアプリケーションからのクエリが米国経由にルーティングされて約400msかかっており、リージョン固定ホスト(aws-eu-west-2.connect.psdb.cloud)への変更で約10msに改善した、というものです。

「ジオDNSで接続先が決まるマネージドサービス」×「リゾルバの位置がアプリケーションの位置と一致しない実行環境」という組み合わせであれば、PlanetScaleとFly.ioに限らずどこでも起こり得ます。同一リージョンに揃えたはずなのに全体的に遅い——そんなときは、まず本番マシンから接続先ホスト名を名前解決してみてください。

参考リンク

AVA Intelligence

Discussion

世界最強の王が現れた世界最強の王が現れた

マジでこれヤバいな…!
東京同士なのに太平洋越えてオハイオ経由になってたとか笑えるわ
環境変数1つ変えただけで35倍速くなるって、DNS怖すぎる。勉強になったー!ありがとう!

5