去年に引き続き参加したISUCON第3回目の予選を通過しました。
結果は参戦1日目の方で4位。 74チーム中、総合6位になりました。
事前準備
再び@cadsと@fruweに声をかけ、去年3位という雪辱を果たすべく、「The Revenge of Mr. Frank & Co.」とチーム名を改名。 今年はチーム数が膨れると予想されてたので去年と違って予選がありトップ20位ぐらいまでしか本戦に行けないという仕組み。方針は以下の通り。
- 言語は我々の得意なruby(goも面白そうだったけど)で勝負
- 去年Railsの再実装で失敗した為、Sinatra(であれば)でやりきる
- Sinatra、Varnish、Sidekiq、Redisの復習
- 計測ツールの導入
- 早い時期に全体のスケジュールを決定する
- 戦略転換の見極め
特に去年の敗因が戦略転換の遅れだったので、それを意識するようにしました。
お題発表
Markdownを使ったコメントシステム。しかもユーザログインがあってポストのprivate/public指定ができるというちょっとアドバンストな作り。
前半戦
今回、サーバはAWSのAMIとして提供され自分のアカウントで起動するやり方だったので、CPUガチャに若干期待しつつ10台一気にlaunchする。確認したら全部同じCPUだったので半分落として、後は本番用、開発用、バックアップ用として残す。
まずは、状況把握の為にひとまず皆でルールの理解とソース解析。ざっと目を通した後はブラウザで動作確認し、今回同一マシン内で実行させるベンチマークプログラムを複数言語で実行。制御方法は去年と同じsupervisord
Perl
2013/10/05 10:31:15 done benchmark
Result: SUCCESS
RawScore: 1020.4
Fails: 0
Score: 1020.4
Ruby
2013/10/05 10:38:11 done benchmark
Result: SUCCESS
RawScore: 2446.9
Fails: 0
Score: 2446.9
Go
2013/10/05 10:45:32 done benchmark
Result: SUCCESS
RawScore: 2840.1
Fails: 0
Score: 2840.1
Node
2013/10/05 10:49:07 done benchmark
Result: SUCCESS
RawScore: 1543.4
Fails: 0
Score: 1543.4
お、rubyそんなに悪くないかも。 デフォルトのperlが遅いのはメモリが温まってないかなと思って再実行したけど飛躍的に良くはならなかったですね。
この辺で予め決めていた開始後1時間という期限になったので作戦会議とスケジュール策定に入る。
まずはVarnishを入れてみようという事なので一人がその作業にとりかかる。もう一人はNewrelicという測定ツールの導入とバックアッププランのRedisバージョンの作りこみ。私はDB周りのチューニングやミドルウェア周り、後は二人のサポート。
DBはMySQLだったので全クエリを吐き出してpt-query-digestで解析して統計的に遅いクエリ順に潰す。 schemaをよく見たらindexがなかったので張ったり、クエリーをちょっと改善してみたり、パラメータを変更(innodb_buffer_pool_sizeとinnodb_flush_log_at_trx_commitのみ)したり黙々作業。
時間はあっという間に過ぎて両方のバージョンを導入してみるもののベンチマークツールからFAILを大量にくらい、スコアが全然つかず。。。
折り返し時点が迫ってきたので、とりあえず今あるチューニングだけのスコアを送信。
2013/10/05 13:43:37 done benchmark
Result: SUCCESS
RawScore: 3791.5
Fails: 6
Score: 3450.2
後半戦
その後、unicornのワーカー数を調整したり、クエリーを更に見なおしたり、initializer作って事前にキャッシュを温める仕組みを作ったりして6000ぐらいのスコアを出した気がする(この辺は作業ログを残すのを忘れていたので記憶が曖昧)
Redisバージョンは雰囲気的に無理そうだったので早々に捨ててこの辺は去年の教訓が生かされてると思う)ESI対応 + Varnishに注力する事を決断。しかし、こっちもエラーの連発。。
最も悩まされたのは
14:53:20 [FAIL] invalid Cache-Control header
14:53:21 [FAIL] invalid post memo URL http://localhost/
どうやらリバースプロクシ用に付与しているヘッダーチェックで弾かれていた模様。
VCLファイルをよく見たら既存のシステムから流用したモノらしく、余計なルールがたくさん入っていたので、仕切り直しということで一旦白紙から再作成する事に。
なんとか基本部分だけ動かす事ができるようになってスコア送信。
2013/10/05 15:45:18 done benchmark
Result: SUCCESS
RawScore: 14694.2
Fails: 0
Score: 14694.2
この辺で暫定一位。
しかしその後privateページのキャッシュexpire方法を模索するも、なかなか進まずにタイムリミットが刻々と迫り、他のチームがどんどん上位に食い込んできて若干焦り始める。ひとまず、強制フラッシュするエンドポイントと裏で定期実行するバックグランドタスク(Varnishでいう所のbanlurker相当)を即席で実装したりしてスコアを伸ばすもトップには届かず敢え無く終了。
振り返ってタイムスタンプを見るとギリギリまで粘っていたのが分かりますね。
-rw-r--r-- 1 root root 2322 Oct 5 17:59 /etc/supervisord.conf
結果、送信できた最終スコアは「20599.5」でした(ローカルスコアは22000ぐらいだったけど)。 予選突破(暫定)はできたものの、まだまだ課題山積な感じです。
良かった点
- 最初に戦略とスケジュールを策定できた
- 去年学んだ捨てる勇気を持てたこと
- 去年よりいろいろ試せたので敗北感は改善
- オンライン参戦なので普段使い慣れてる環境で落ち着いて出来た(本戦はアウェー)
- 今年の密かな目標であるモリスさんに勝ったこと(予選だけど)
反省点
- 作業ログをもっとしっかり取るべき
- バージョン違いはgit mergeせずにbranchをcheckoutするべき
- lingrサポート見ればよかった
- MySQL5.6のMemcache APIの存在には気づいてたけど、罠とは知らなかった
- Newrelic導入に時間かけた割には得られる情報量が薄かった
- リバースプロクシが遅れたのは全実装して投入を試みたからでincremental apporachの方が良かった
- 全員のタスクマネジメントとペアプログラミングをもっとすれば良かった
今回も前回同様、非常に楽しめました。 運営の皆様、お疲れ様でした&本戦はさらなる期待をしてます!
おまけ
後日行われた各チームの反省会で判明したのがworkloadの存在。どうやらベンチマークツールの並列度を上げる事ができるらしく、そこそこ速くなっているシステムだったらスコアが1.5倍ぐらいは伸びたかもとの事。最終スコアを考えると、30000点台のトップを取れたかもしれないのが無念。。アプローチ自体は総合トップの@sechiroさんと似ていたので方針は間違ってなかったかと。