本番環境などでやらかしちゃった人 Advent Calendar 2023に参加しようかと思ったのですが、あちらはどちらかというとインフラ回りのトラブルのお話が中心。この話は開発よりのトラブルなのでちょっと違うかなぁということで参加は取りやめて、普通の記事として投稿します。
このトラブルは過去の職場での体験なので、現在はソースコードやドキュメントが参照できません。そのため記憶に頼って書いています。また、フェイクもあちこち入れていますので、全体として辻褄合わない話になっているかもしれません。一応、トラブルの本筋のところは外さないようには書いているつもりではありますが。その点、ご認識の上で読んでいただけると助かります。
どういうサービスでどういう改修か
とあるユーザ投稿型のサービスで、ユニークユーザ数は100万人程度とそこそこの規模です。このサービスに以前から「お勧め投稿」という機能があったのですが、そちらの機能強化の改修でした。
今までは単純に各投稿から閲覧数が多いものをお勧めしていたのですが、もっとお勧め精度をあげようということで、いいね数もお勧めスコアに算入することになりました。また、これまでは全期間をスコア対象にしていたのですが、直近の閲覧やいいねのみに絞るようにもしました。
お勧め投稿はサービス内で単一ではなく、複数の要素の組み合わせごとに存在します。例えて言うなら「関東地方在住でアニメ好きなユーザ用のお勧め」とか「関西地方在住でITに興味があるユーザ向けのお勧め」という感じですね。要素の組み合わせは数万種程度存在します。またお勧め投稿は要素の組み合わせごとに100件程度の投稿がリストアップされていて、トータルで数百万件の投稿がお勧めされる構成になっています。
惨劇の状況
機能改修が完了し、テストもパス。本番環境にリリースして、意気揚々とお勧め投稿更新バッチを実行したところ、しばらくして監視アラートがあがってきました。
監視ダッシュボードを確認したところ、DB の負荷があり得ないくらいあがっていました。そして無数の 500 エラーの発生。DB から反応が返ってこないため、一部の静的ページや DB 参照しないページを除いた、ほぼ全ページがエラーとなってしまい、事実上サービス全体がダウンしてしまいました。
慌てて切り戻しを行ったところ、徐々に DB 負荷が下がり、それに伴ってページ表示も復活。10分後くらいには監視閾値も下回り、サービスは復旧しました。
惨劇はなぜ起こってしまったのか
一言でいうと、お勧め投稿リストを作るためのクエリが重すぎてスロークエリになってしまったためでした。同時に数万のスロークエリが DB に投げられたため、 DB が事実上ダウンしてしまったのです。
実はお勧め投稿リスト自体は DB には持っておらず、キャッシュ(memcached)に格納していました。キャッシュにデータがある場合にはそのデータを使って表示し、キャッシュの期限が切れたあとの初回アクセス時に DB にアクセスしてお勧め投稿リストを更新するという仕組みです。当然にお勧めリストの更新タイミングがバラバラになってしまいますが、それは問題ないという仕様でした。
お勧めリスト更新バッチは、このキャッシュを一気にクリアするものです。普段は散発的に更新されていたお勧めリストが一気に更新され、その全てがスロークエリになったために DB に過負荷を与えてしまったという仕組みです。
惨劇を起こさないためにできること
負荷試験をしっかり行うということに尽きます。が、これは言い訳になってしまうのですが、当時は本番相当の負荷試験を行う環境がありませんでした。
今回の障害は本番環境のデータ量とアクセス量が重なり合って発生したものです。しかし検証環境の DB にはスカスカのデータしか入っていなくて、まともな負荷検証が出来る状態ではありませんでした。
本番のデータコピーを検証環境にも適用しようという話は以前から出ていたのですが、ずっと先送りになってしまっていました。このあたり、開発チームとインフラチームが完全に別部署に分かれていて風通しがイマイチよくなかったというのも間接的な要因としてあげられるかもしれません。
繰り返しになりますが、検証環境が無かったというのは所詮は言い訳でして、本番と全く同じでなくても、少なくともデータ量だけでも本番相当になる疑似データを検証環境に用意して負荷検証を行うべきでした。