前のエントリ #isucon で優勝してきました は当日夜に酔っ払った頭で勢いで書き上げたので、少し冷静に振り返ってまとめてみます。
最初のボトルネック発見
- DB が CPU 4コアをフルに使って回っているのですぐに Query が重いのは分かった
- 重いクエリはキャッシュすれば、という発想は自然 (実際 MySQL のクエリキャッシュだけでスコアは 1.5倍程度上がる)、とはいえ
ということで、DB のテーブル構成を変更して高速化できないか、という作業を優先。
アプリケーションサーバの CPU ネック
- この時点でスコアが 20,000 / min 程度
- 全体の 80% 程度がアプリケーションへのアクセス
- つまり 20,000 / 60 * 0.8 = 266 req/sec で処理できている
- DB を引いて、テンプレートをレンダリングする Perl の Webapp として、この速度はかなり速い
- ここを攻めても数倍の改善をするのは難しいだろう
ということで、アプリケーション自体をあれこれするのは手段から外し、フロントで cache させる作業を優先。
想定される優勝ラインが 100,000 / min、つまり 1,666 /sec で、これは Starman で何もしない Hello World を実行する程度の速度。そこまで高速化するのは無理という判断です。
Apache -> nginx への切り替え
通称「kazeburo の罠」への対処
ip_conntrack 溢れへの対処
- 大量のリクエストを捌くときには実環境でもありがちなので、最初から頭にあった
- 問題が起きたら off にしよう、と思ってた
- スコアが不自然に落ちたので dmesg を見たら案の定、だったので迷わず off
nginx -> memcached ローカルポート枯渇への対処
- nginx のエラーログになにやらメッセージが
- それでググったらすぐに ローカルポートを食いつぶしていた話 - ダウンロードたけし(寅年)の日記 がヒット
- 接続を永続化するオプションは思いつかなかったので、ローカルポートの範囲拡大と TIME_WAIT を短くして対策
「サイドバーの罠」への対処
- これは現象を把握するまで1時間ぐらい悩んだ
- おそらく ESI 的な対処が最善だろう、と見当は付いたものの、使ったことのない varnish を導入してはまったら確実に時間切れ
- この時点で 30,000 / min までいっていたので、最悪 GET 時の cache off にして安全策をとっても 2〜3位にはなれるかな (弱気)
その後公開された ベンチマークツールのソース をみたところ、「毎秒1回更新されるリストの最新10件中、どこかに反映されていれば OK」というロジックだと判明しました。
つまり、最大で 10秒弱までは cache を保持していてもチェックが通るはず。
これは作業中の、
- 5秒は完走
- 10秒は完走することもあるが、まれに失敗する
という現象とも符合します。
とはいえこの時点ではチェックロジックは未知のためかなり悩み、最終的にベンチマークが完走するかどうか、不安をもって最終判定を迎えることになりました。
事前βで最高スコアをたたき出したのは nginx で SSI を使う、という目から鱗というか、SSI とか10年ぐらい使ってないので自分は考えもしなかった構成だそうで。いいことを聞いたので、自分でも検証してみたいと思います。