「タイムアウトしました」と表示された画面を前に、ユーザーからサーバーまで何段も経由していることを思い浮かべながら、どの段で時間切れになったのかを落ち着いて考えている一人運用の保守担当者

タイムアウトはどこで起きているか|多段構成の切り分け

「さっきの画面、しばらく待たされた末にエラーになったんですけど」——保守運用をしていると、この「タイムアウトしました」という報告に何度も出くわしますよね。やっかいなのは、時間切れは一つの場所で起きるのに、リクエストはいくつもの段を経由していることです。ブラウザ、ロードバランサー、nginx、アプリ(php-fpmなど)、DB、外部API——どこか一段で時間切れになっただけでも、ユーザーには同じ「タイムアウト」に見えます。

だから、どこの数字を延ばせばいいのか分からず、ついあちこちのタイムアウト値を手当たり次第に延ばしてしまいがちです。でも、それは詰まりの元を奥に押し込むだけで、根っこは残ります。まず必要なのは、「今回はどの段で時間切れになったのか」を一つに絞ることです。

この記事では、多段構成のタイムアウトを「一番短い制限が先に切れる」という考え方を軸に、手前(ユーザー側)から奥(DB・外部API)へ順番に、どの層で切れたかをログから絞る手順を一緒に整理します。構成(Apache、nginx+php-fpm、ロードバランサー+アプリサーバーなど)で細部は変わりますが、考え方はそのまま使えます。

結論:多段構成では、経路の中で一番短いタイムアウトが最初に発火します。だから直すべきは「一番短い値」ではなく、「実際に時間切れを起こした一段」です。手順は、①いつ・どのくらい待たされて切れたか(体感時間)を押さえる → ②各層のログで「timed out」の記録が出ている段を探す → ③その段のタイムアウト値と、上流の遅さのどちらが原因かを見分ける。値を延ばすのは応急処置と割り切り、本筋は「なぜその段が待たされたか」に置きます。むやみに全部の値を延ばすと、遅い処理が居座って受け口を長くふさぎ、かえって全体が詰まります。

何が起きているか:タイムアウトは「一番せっかちな段」が先に切れる

タイムアウトは、「決めた時間内に相手が返してこなかったら、待つのをやめて切る」という約束事です。多段構成では、この約束事が段ごとに別々に設定されています。ざっくり並べると、こんな段があります。

ここで大事なのは、一番短い制限を持つ段が、最初に時間切れになるということです。たとえばアプリの処理は60秒まで許されていても、手前のnginxが30秒で切る設定なら、ユーザーには30秒で504が返り、アプリはまだ裏で処理を続けている——という食い違いが起きます。逆に、nginxは長く待つのにDBのクエリ制限が短ければ、切れるのはDBの段です。

つまり「タイムアウトした」という一言の裏では、その経路でいちばんせっかちな段が犯人役になっています。次から、その一段を絞る順番を見ていきます。

手前から奥へ、どの段で切れたかを絞る

ブラウザ・プロキシ(nginx)・アプリ・DBや外部APIという多段の経路のうち、一番短いタイムアウトを持つ一段が先に時間切れになることを示した流れ図
経路の中で一番短い制限を持つ一段が先に切れる。直すのは「切れた段」

ポイントは、どの段のタイムアウト値が発火したかを先に特定してから、値をいじることです。奥からいきなり延ばすと、原因でない段まで触って回ることになります。

① 「どのくらい待って切れたか」を押さえる

最初の手がかりは、時間切れになるまでの体感時間です。ここが、どの段が切れたかを当てる近道になります。

「何秒で切れるか」を先に押さえるだけで、次に開くログの当たりがつきます。

② 各層のログで「切った段」を探す

体感時間で当たりをつけたら、その段のログに時間切れの記録が出ているかを確かめます。切った側は、たいてい正直に記録を残しています。

ここで注意したいのは、「切った段」と「遅かった段」は別のことがあるという点です。nginxが504を出していても、遅さの元はその奥のDBかもしれません。nginxのログは「後ろが時間内に返さなかった」と教えてくれているだけで、なぜ返さなかったかは、さらに奥を見て初めて分かります。

ここで一呼吸。秒数を聞いてログを開くのは遠回りに感じるかもしれませんが、「どの段で切れたか」を先に知るための一番の近道です。焦って全部の値を延ばす前にログを見た、その判断がすでに落ち着いた一歩です。

③ 「値が短すぎる」のか「上流が遅い」のかを見分ける

切った段が分かったら、最後に原因の向きを見分けます。ここを取り違えると、直したつもりが逆効果になります。

値をいじる前に、いまの値と状態をメモしてから動きます。「元に戻せる状態」を保ったまま、一段ずつ確かめます。

具体例:「30秒ちょうどで切れる」を追う

よくある報告を、順番に切り分けてみます。

もし最初に「とりあえず全部のタイムアウトを延ばす」をしていたら、一時的に表示は戻っても、データが増えるたびにまた同じところで切れ、しかも重い処理が長く居座って他の画面まで遅くしていたはずです。「どの段が・なぜ切れたか」をたどれたからこそ、次に活きる直し方にたどり着けました。

影響:切り分けの順番を持つと、何が変わるか

タイムアウトの切り分け順を1枚持っておくと、直す力そのものより先に、焦りと手戻りが減ります

逆に、毎回いきなり全部の値を延ばしていると、詰まりが奥に溜まっていき、ある日まとめて大きく効いてきます。順番は、その日の自分を助ける道具です。

明日やること:自分の現場の「タイムアウト一覧」を1枚書く

立派な資料は要りません。明日できる、いちばん小さな一歩はこれです。

  1. 自分が担当するサイトで、リクエストがユーザー→どこ→どこと流れるかを書き出す(例:ブラウザ → nginx → php-fpm → MySQL、途中に外部API)。
  2. それぞれの段のいまのタイムアウト値を1行ずつ控える(nginxの proxy_read_timeout / fastcgi_read_timeout、PHPの max_execution_time、DBやAPIクライアントの待ち時間など)。値が分からない段は「未確認」と書いておくだけでも十分です。
  3. その一覧を眺めて、一番短い値がどこかを確認する。そこが「まず切れる段」の候補です。
  4. あわせて、各段のタイムアウトログの場所と、時間切れを示す文言を1行メモする(nginxの upstream timed out、PHPの Maximum execution time など)。
  5. 次にタイムアウトの報告が来たら、体感時間 → この一覧 → ログ、の順にたどる。効いた段と原因を書き足していく。

きれいに描かなくて大丈夫です。「どの段が、何秒で、どんなログを残して切るか」の一覧が1枚あるだけで、次の自分(や引き継ぐ人)が、全部の値を手当たり次第に延ばすループに迷い込まずに済みます。

タイムアウト切り分けチェックリスト

切り分けに着手するとき、これだけ確認できているかを見る項目です。コピーして、自分のメモに当ててみてください。全部を毎回そろえる必要はありません。

まず外せない最低ラインはこの3つです。急いでいても、ここだけは押さえます。

次の項目は、手前で片づかないとき・原因が奥にありそうなときに追加で確認します。当てはまらなければ飛ばして大丈夫です。

全部に○が付かなくても大丈夫です。最低ラインの3つ、とくに「値をいじる前に、どの段が切ったかを一つに絞る」さえ押さえられれば、手当たり次第に数字を延ばして消耗するより、ずっと確かな一歩になります。

よければ、こちらも

タイムアウトの切り分けは、「どの段で・なぜ待たされたか」をたどるほど楽になります。近い5xxの見方や、奥の遅さの調べ方をセットにしておくと、次の「タイムアウトしました」がだいぶ軽くなります。

タイムアウトの原因が一つの段の設定と上流の遅さだと突き止め、正常に戻った画面を見て肩の力が抜けた保守運用の担当者

タイムアウトがこわいのは、「時間切れ」という結果は一つなのに、切れうる場所がいくつもあって、どこを直せばいいのか見えないからです。でも、「一番せっかちな段が先に切れる」と考えて、体感時間からログへ順にたどると決めるだけで、霧はかなり晴れます。多くの場合、全部の値がおかしいわけではなく、ただ一段だけが、実態より短い時間で——あるいは奥の遅さに引っぱられて——先に切れていただけです。

今日は、自分の現場の「タイムアウト一覧」を1枚書いてみるところからで十分です。その1枚が、次の「タイムアウトしました」を、総当たりではなく手順に変えてくれます。

ほかの実務ヒントは記事一覧からどうぞ。保守運用の小さな備えを、メールでも少しずつお届けしています。

関連用語