#isucon 2014の予選をほぼ一人で戦うハメになった話
AMI提出した!2人目が起きたのが14時、3人目は17時という厳しい状況の割には頑張ったと思う。10位には入れなかったけど。めっちゃ楽しかったー!運営の方、ありがとうございました #isucon
— かと (@orangain) 2014, 9月 28
まとめ
準備したこと
事前にチームで昨年の問題を解きました。プロファイリング方法を全然わかってなかったので、制限時間内にほとんどスコアが伸びませんでした。1週間かけて勉強し、MySQLのスロークエリログやApache/nginxのログを集計する方法を知りました。
また、継続的にプロファイリングできるようにするのが大事だと痛感したので、以下の様なツールを作りました。
- ベンチマークを回すと同時に、以下の様なデータを収集する。
- 収集したデータを集計し、Jenkins上で成果物として保存し、スコアのグラフをプロットする。
やったこと
10:00-
開始時刻になっても他の2人が現れないので、一人で作業することに。最初のところで、案内された手順でRuby実装が終了しない問題や、isuconユーザーでSSH接続できない問題などでかなり手間取ってしまいました。
ブラウザでの挙動、DBの構造、稼働中のミドルウェアを確認をした上で、事前に準備したツールでリクエストログやスロークエリログの集計などを行いました。
Python実装での初期スコアは 1534 でした。
11:00-
アプリのソースコードを読み、データ構造を変えたらBAN/Lockの判別はO(1)にできるし、JOINとかないからKVS向きだと判断しました。が、上位陣はMySQLのままで高いスコアを出していたので読みが甘かったかもしれません。
インメモリで高速に処理できてかつ永続化もできるKVS、ということでRedisを選択しました。Redisはこれまで話を聞くだけで実際に使ったことなかったですが、Pythonから簡単に使えました。tagomorisさんがISUCONは新しいことに挑戦できる場だと仰っていたのはほんとそう思います。 *1
12:00-
ご飯を食べ、Redisをインストールしました。yumでインストール出来ないのは辛かったです。Redisの使い方を勉強しながらアプリを書き換えました。ちゃんと書き換えられるか自信がなかったので、少しづつ書き換えて確認する戦略をとりました。
とりあえずlogin_logの処理とBAN/Lockのチェックだけを書き換えて、Failしつつもスコアが 8864 に上がったのが13:30頃でした。
14:00-
14:00になってようやく2人目のメンバーが参加しました。が、すぐに状況を把握できるわけではありません。。
login_logに初期データがあることに気づき、reportが通るようになったのが14:20頃。レポートの出力順をチェックされていたらどうしようとドキドキしてましたが、無事SUCCESS。スコアは 9250。
Redisのデータがちゃんと永続化されているか自信がなかったので、サーバーを再起動して問題ないことを確認。
nginxで静的ファイルを返すよう設定してもらいました。
15:00-
最後までMySQLで処理していたログイン時のユーザー情報取得もRedisに移し、ついでにパスワードは平文で比較するようにして、スコアは 9623 に。
初期化処理でMySQLからRedisにデータをロードする処理が遅くてイライラしてきたので、予め初期化済みのaofファイルを用意して置き換えるようにしました。これによって軽快にベンチを回すことができるようになりました。
nginxで静的ファイルを返すとContent-Typeが正しく設定されないという罠に引っかかって二人で直しました。
16:00-
この辺でworkloadを増やすとスコアが上がることに気づくものの、cannot assign requested addressというエラーが出るようになります。
カーネルパラメータを調整し、workload 10でfailしなくなったのが16:30で、スコアは 30694。
RedisをUnix Domain Socketで繋いだら速くなるかなと設定してみてもそれほど変わらず、スコアは 31059。
nginxのworker_processesを4に増やすと 31650 に。この辺からスコアが伸びなくなります。
17:00-
nginxとgunicornをUnix Domain Socketで繋ぐも、さほど変わらず。
セッションクッキーがない場合だけnginxで静的ファイルを返すよう設定を試みましたが、二人共詳しくなかったため、なかなかうまく設定できず。
そんな中3人目がようやく登場するも、なにができるわけでもなしw もう諦めようかと言いながらも、どうにかして設定したのが 17:40。スコアは 32710。これが最終スコアになりました。
この後、いろいろなworkload、gunicornのプロセス数を試すも、スコアは上がりませんでした。高速化できる場所も思いつかずに終了しました。
反省点
後半ちゃんとボトルネックを特定できておらず、ほとんどスコアが上がらなかったのは反省点です。サーバー起動時にオンメモリにデータを持つようにすればもっと上がった気がします。
ツールを用意したものの、肝心のnginxのレスポンスタイムを出力するのを忘れていたりと全然活用できませんでした>< 自分が直前に作ったコマンドは使い慣れてないし、微妙に使いやすくないし、心の何処かで自分を信用できてなくてほんとダメでした。
というか、Redisへの書き換えに3時間ほど集中したおかげで色々吹っ飛びました。こういう時に冷静な仲間が居るといいんだろうなと思います。
まとめ
今回はじめて参加しましたが、非常に楽しい一日を過ごすことができました。運営の皆様、チームのメンバー、ありがとうございました!
*1:supervisordも去年の問題を練習で解いた時に初めて使い方を知りました。