再試行は入れていた。それでもデプロイが落ちた
再試行の処理は、半年前から入れていました。CMSが500を返したら、少し待ってもう一度投げる。3回まで。
それでも、デプロイが落ちました。
再試行を入れてあったのに効かなかった話です。何が返ってきていたのか、なぜ3回では足りなかったのか、そして回数ではなく間隔を増やした理由を書きます。
ページを先に作る構成では、ビルド中に問い合わせが集中する
このサイトは、訪問者が来てからCMSに聞くのではなく、デプロイのときに全ページを作ってしまう構成です。表示は速くなりますが、そのぶんビルドの最中にCMSへ何十回も問い合わせが飛びます。
記事が増えたいまは、1回のビルドで82ページぶんの問い合わせが、1分足らずのあいだに走ります。
動的に表示していたころは、1回の問い合わせが失敗してもその1回の表示が失敗するだけでした。次に開けば直ります。
事前生成に変えたあとは、同じ1回の失敗がデプロイ全体を止めます。新しい記事も、直したところも、まとめて出なくなります。
速くするために取った選択が、別の場所の壊れやすさを生んでいたことになります。
返ってきていたのは、WordPressのエラーではなかった
ビルドのログを見ると、82ページ中40ページを作ったあたりで止まっていました。
Error: GraphQL Error (Code: 500): {"response":{"error":"<!DOCTYPE HTML PUBLIC ...
500という数字だけ見ると「WordPressが処理に失敗した」と読みたくなりますが、中身はHTMLでした。しかもWordPressのエラー画面ではなく、Apacheが出す素のエラーページです。
| 返ってきたもの | そこから分かること |
|---|---|
| WordPressのエラー(JSON) | WordPressまでは届いている。中で失敗した |
| Apacheの素のエラーページ | WordPressに届く前に断られている |
つまりPHPが動く手前の段階で弾かれていました。短時間に問い合わせが集中したことで、共用サーバー側がまとめて断る状態に入っていたのだと思います。
3回では足りなかった理由
入れてあった再試行は、こういう設定でした。
| 回 | 待ち時間 | 累計 |
|---|---|---|
| 1回目 | — | 0秒 |
| 2回目 | 1秒 | 1秒 |
| 3回目 | 3秒 | 4秒 |
最初から最後まで、4秒で終わります。
相手が「一瞬もたついた」だけなら、これで通ります。実際これまでは通っていました。今回そうならなかったのは、断られている状態が数秒では明けなかったからです。
再試行は動いていました。ログにも3回ぶんの失敗が残ります。
ただ、4秒のあいだに3回投げても、3回とも同じ状態の相手に当たります。回数を増やしても、待っている時間が短ければ結果は変わりません。
回数ではなく、間隔を増やした
直したのは、待ち時間の表です。
const MAX_ATTEMPTS = 5;
const RETRY_DELAY_MS = [1000, 3000, 8000, 15000];
最後まで粘ると、1件あたり最長27秒かかります。ビルドは確実に遅くなります。
それでもこちらを選んだのは、待って通るほうが、デプロイごと落ちるより安いからです。27秒は待てば終わりますが、落ちたビルドは自分では直りません。気づいて、原因を調べて、押し直すまで止まったままになります。
回数だけ10回に増やしても、間隔が1秒なら全体で10秒です。混んでいる相手にとってはさらに叩きに来ただけになります。
相手が回復するのを待つのが目的なので、増やすべきは回数ではなく間隔のほうでした。
気づくのが遅れた理由
この失敗に気づいたのは、デプロイした数時間後でした。新しく足したページを開いたら404だったからです。
ビルドが失敗しても、サイトは前の版のまま動き続けます。これ自体は正しい挙動で、壊れた版が公開されるよりずっと良い。ただし見ている側には何も起きません。止まったことは、止まった画面を見ても分からないということです。
いまは通知を入れていませんが、次に同じことが起きたときに数時間気づかない状態は残っています。ここは別に手を打つ必要があると考えています。
今回の学び
まず、速くする変更が、壊れやすさを持ち込むことがあるということ。事前生成そのものは良い選択でしたが、「1回の失敗が何を止めるか」が変わっていました。変えたときに、失敗の重さも一緒に変わります。
次に、500の中身を見ないと、誰が断ったか分からないこと。同じ500でも、アプリが失敗したのか、その手前で断られたのかで打つ手が違います。今回は中身がHTMLだったことが手がかりになりました。
そして、再試行は「回数」ではなく「どれだけ待つか」だということ。入れてあること自体は安心材料になりません。相手が何秒で回復するのかを考えないまま回数だけ決めていた、というのが本当の原因でした。
この記事を共有する
関連記事

ソース解説(5) 文字列を自分で組み立てる場所
このサイトで、HTMLを文字列として自分で組み立てている場所は2つあります。RSSフィードとサイトマップです。 画面に出るページは、部品を組み合わせれば勝手に出来上がります。この2つは違います。1行ずつ並べて、最後に文字 […]

測れるもので代用すると、測りたいものから離れる
記事を最後まで読んだ人を数えたいだけでしたが、判定を4回作り直しました。そのたびに別の壊れ方をした経緯と、そもそも何を測ろうとしていたのかを整理します。

通ったビルドが、何も直していなかった
codegenもビルドもコミットもpushもデプロイも、すべて成功した。それでも直したかったものは1つも直っていなかった。変更が丸ごと消えたことで辻褄が合ってしまい、検査をすり抜けた話と、それを見つけるための確認について。