ISUCON4 予選に参加した

予選に参加して惨敗した

9/27の一日目に参加して最終スコアはworkload 8で33,000ぐらい。

惨敗してかなり悔しい。根拠もない自信で予選くらいは思いつく実装を全部できればなんとかなるやろと甘く見てた。。 本戦出場の最終的な結果は10/6辺りに発表されるらしいけど、まー無理ですね。

予選終わったときの敗北感とか疲労が半端無かった。でも、最高に楽しかった。来年リベンジする。 この企画マジで素晴らしい。F1もそうだけどコンテストによって人類は進化するのでよい企画だと思う。 運営のみなさんには本当にありがとうと言いたい。

メンバーと選択した言語

参加メンバーは @bluerabbit777jp, @najeira, @Jxck_ で言語はGoにした。

二年前のISUCONで@najeiraと出ようって話をしてたけど、その年は都合が悪くなって出られず、やっと今年出れた。

出ようって事は2年も前に決まってたけど、実は言語が決まってなかった。 @najeiraはGo, Pythonが得意で私はJava, C#, ruby, nodejsが普段使いの言語だったので実はミスマッチしてた。。 Goは素晴らしい言語だーと思ってた事もあってISUCON駆動でGo覚えられる!と前向きに考えてGoにした。

Goを使えて二人とも知っていて、一緒に参加して欲しいなーと思ったのは @Jxck_ だった。 しかし、GoCartというイベントとぶつかっていて可能ならリモートで参加してもらうという事にした。

なので、予選は実質ほぼ @bluerabbit777jp, @najeiraの二人で戦った。 本線はフルで @Jxck_ も参加する予定だったのに出番がなくなってしまい申し訳ない。

事前にやっておいたこと

初のISUCONだったので@najeiraとISUCON夏期講習を事前にした。 そこで、下記はやらないといけないなと把握した。

  • 不変なデータは全部インメモリに入れる
  • 可能な限りstatic filesに書き出してnginxで静的ファイルとしてできるだけ捌く
  • 初期データ投入用のshellがあり、そこでDBのスキーマ変更だけでなく、事前にできることをする
  • できればMySQL捨ててRedisに載せ替える
  • MySQLを使うなら検索は検索結果テーブルを作成してSELECTはシンプルにしてINSERTを多くする

GoでRedisを使ったことがなかったので事前にライブラリを調べるなどした。

下記も事前に用意した。

振り返ってみるとこれだけじゃ全然足りなくて

  • nginxやmysqlなどの設定ファイル
  • ack, tmux, emacs, shellの各種設定ファイル
  • golang/nginxのプロファイリングのためのツール

なども事前に用意しておくべきだった。 他のチームは秘伝のタレを用意しててchef流すだけにしてた、とか賢い。

予選当日

仕様把握と設計

最初にgithubリポジトリにソースをpushして@najeiraに各ハンドラ毎のSQLを全部Issueに書いてもらって 仕様を把握できるようにしてもらってる間に私はMySQLの初期データ量/設定値/テーブル構成をIssueにメモしていった。 予習でベンチマークツールでどのテーブルにどれだけINSERTするのか、INSERT/UPDATEが無いテーブルであれば オンメモリに載せられると思っていたので、ベンチマークツールを実行する前後のデータを記録するなどした。

記録はこんな感じ

f:id:bluerabbit:20140929232700p:plain

f:id:bluerabbit:20140929231117p:plain

f:id:bluerabbit:20140929231125p:plain

f:id:bluerabbit:20140929231138p:plain

11:00頃にはだいたい初期の準備と設計は終わって初期実装は下記のようにしようと決めた。

  • ロック情報は別テーブルに保持
  • RDBでカウントしたら負けなのはわかりきってたので、カウントはRedisのINCRを使う
  • /mypageの成功直近2件の情報はidでソートして2件取得する
    • この時点では最終的にはMySQL捨てるかも知れないしとりあえずこれでいいやと思ってた
  • init.shで成功レコードをテーブルにINSERTしておく
  • init.shでRedisに過去login_logの状態を復元しておく

f:id:bluerabbit:20140929231306p:plain

f:id:bluerabbit:20140929231645p:plain

f:id:bluerabbit:20140929231656p:plain

/login

f:id:bluerabbit:20140929231849p:plain

f:id:bluerabbit:20140929231947p:plain

実装

担当を分けて私はアプリケーションのコードを修正して、@najeiraにnginxで静的ファイルを捌くように設定変更などをしてもらいつつinit.shでRedisの初期化を行ってもらった。

ココまでの第一実装が終わったのがコミットログを見ると14:20頃。ココからまだ残っている改善点を対応していけばいいかなーと思っていたが、思うようにスコアが伸びない。ベンチマークを実行するとfailしていて全然スコアが出ない。

静的ファイルをnginxで配信するようにしたが、直接URL叩くとcssは取得できるのにcssが効かないことがわかってアタフタし StackOverflow先生に助けられてinclude /etc/nginx/mime.types;だと気付いてやっとスコアが出たのが約3万点。

スコアやっと出たと思ったが、ポータルにスコアが記録されない。運営に確認すると/reportがバグっていることに気付くがバグが中々見つからず、次の改善に着手できない。バグをつぶしたのが17:00過ぎでもう他の対応をする時間がなくなり、nginxやmysqlの設定を変更してworkload数がいくつくらいがいいのかを何度も実行している内に時間になって終了した。無念。

結局、最初の一次実装までしか改善できなかった。最初にusersは不変なテーブルだから全部オンメモリに置いとくとか、全部Redisにのせようとか、ベンチマーク実行時のログを元にベンチマークの癖とどこを改善しないといけないのか見ようとか、思ってたのも時間も集中力もなくなってしまい、やりたかった事が全然できずで終わってしまった。

競技終了後

他にGoを選択した人のブログで知ったがGOGC、MARTINI_ENVの設定があると知る。 実際にGOGC=off MARTINI_ENV=production付きでベンチマークを実行したら41000を超えた。MARTINI_ENVを設定しない場合は開発モードになって毎回htmlのテンプレートファイルをパースしに行ってしまうようだ。GOGCでoffすると早くなるとかunicornrspecGC止めるテクニックぽくて面白い。

競技終了後も各参加者のブログでどんな改善をしたとか、どんなツールを使ったなどを知れるのも面白い。参加すると競技中だけでなく競技後も楽しめるので一度で二度美味しい感じですね。

やっぱり二人より三人の方が時間にも精神的にも余裕が出ていいので、来年は三人で出てリベンジしたい。