ビューカウンターがカタログを502に変えた話
配送
販売中の全キットを一覧表示するページは、ローンチ後約41時間にわたり、訪問者全員に502 Bad Gatewayを返していました。各キットの個別ページは正常で、ホームもチェックアウトも動作していました。壊れていたのは、ページビューをカウントする数行のコードでした。
バグの内容
当ストアのサーバーはキットページの訪問数をカウントします。どのキットへのリクエストかを特定するため、パスの第2部分を取得していました:
self._count_view(path.strip("/").split("/")[1] if path.count("/") >= 2 else "")
/kits/studio/ならstudioです。カタログ自体の/kits/では、パスにスラッシュが2つあるためガードを通過しますが、スラッシュを除去すると部分文字列は1つしかなく、[1]でIndexErrorが発生していました。ハンドラーはPython標準のhttp.serverです:do_GET内で例外が発生すると応答なしで接続がクローズされ、Cloudflareは訪問者に502を表示していました。
修正は、部分文字列が存在しない場合に空文字列を返す小さな関数1つです。ビューカウントが必要なのはキット名だけであり、カウント対象のページを壊す必要は全くありませんでした。
なぜ既存のチェックで検出されなかったのか
- 稼働監視はホームページのみを開いていました。 ホームページは正常でした。
- リリース手順では出荷する各キットを確認していました: そのページ、チェックアウトへのリダイレクト、有料パスをエンドツーエンドで確認していました。全キットの一覧は開いていませんでした。
- アナリティクスでは検出できませんでした。 カタログの訪問者をカウントするはずのコードこそがクラッシュしていたためです。サーバーログには答えがありました:その41時間で17件のトレースバック、それぞれが人またはクローラーからのリクエストで、ページではなくエラーを受け取っていました。
変更点
- 全ページを、外部から、毎時確認します。 小さなジョブがサイトマップを読み、Cloudflare経由で訪問者として全英語ページと、他の42言語のうち1つずつを順に開きます。連続して2回失敗するとメールを送信し、復旧すると別のメールを送信します。
- リリース手順では、サイトの再ビルド後に全英語ページを開き、1つでも200を返さなければ完了を拒否します。
- インデックスページもページとして扱います。 他のページを一覧表示するページは、訪問者が最初に到達する場所であり、誰もテストしていなかったのはまさにそれらでした。
まとめ
リクエストハンドラ内でのカウント、ロギング、トラッキングなどのサイドワークは、リクエストを失敗させてはならず、変更したページだけをカバーするページチェックでは、それらへリンクしているページを見逃してしまいます。Night Watch のウォッチは、ホームページだけでなく、含まれるべきテキストを指定した任意のページリストを受け取ります。