ISUCON 2014 の思い出

ISUCON 2014 という、いい感じにパフォーマンスチューニングしてスピードアップさせるコンテストの予選に参加しました(詳しくは http://isucon.net/archives/cat_1024989.html)。ポータルに送った時のスコアは 15000 くらいで、一番良かった時は 19000 くらいでした。結果としては惨敗です。

チーム名は「ドラゴンチャーハン」で、@lesamoureuses と @tana_ra と僕が適当に集まって社内チームを組みました。

この記事のまとめとしては、ISUCON 楽しかった!が、惨敗で悔しいので来年も頑張りたい!です。

反省メモ

以下は社内振り返りのために殴り書きしたメモをもう少し読みやすくしたもの。読みにくいと思いますが、来年参加する人の参考になればラッキーです。

予選に向けた練習

ISUCON 2014 夏期講習の AMI を使って練習(去年の予選)しました。

ISUCON 夏期講習 2014 を開催しました : ISUCON公式Blog

2 回集まって練習し、前半はアプリケーションの把握、後半(前日)はチューニング。 前日に Varnish とかを使ってスコアが大きく上がってワイワイしてました。

役割分担

フロントエンドや DB 周りの調整を @lesamoureuses さん、アプリケーション周りでコード書くのを @tana_ra と僕。 このメモはアプリケーション側をいじってた僕の目線で書かれています。

チューニング要点

  • Varnish で /mypage をキャッシュしたり、前日に用意していた秘伝のタレ(設定ファイルの調整)をプロファイリング後に入れる
  • できるだけメモリに乗っけるようにコードを書き換える
    • ストレージが HDD であることと、メモリが 7.5 GB くらいあると聞いた時点で、I/O を発生させないようにする方針になった
    • 後述のインテグレーション失敗で全部はできなかった

序盤

  • 午前中
  • 人数分のインスタンス起動・webapp 以下を Git 管理
  • プロファイリングの準備
    • Nginx の設定を前日に用意したものに
    • LTSV で吐いて、ログを解析しやすくする
    • rack-lineprof を入れる
  • プロファイリング
    • 当然 /report が遅い。 次いで /mypage が遅い
    • /report のレスポンスは加点しないということレギュレーションに書いてあったが見落としていたため、ここをチューニングする方針になった
  • アプリケーションの理解・戦略を考える
    • Google Doc で共同編集しながら、リクエストパスとその挙動をまとめたり、メソッドごとの挙動をまとめたりした

中盤

  • 16 時ごろまで
  • Redis のセットアップ
  • Ruby から Redis を使う方法など変更の方針を共有
  • Redis を選んだのは前日に練習で使っていたのと、reds-objects に Redis::Counter というのがあって、ログイン失敗回数をインクリメントしていくのに便利そうだと考えたため(Redis について全然知らなかった)
    • 実際はひとつのキーに対してしかインクリメントできないものだったため使えなかった
    • この時点で memcached にしても良かったし、Ruby の Hash にぶち込む実装にしても良かった
  • 再起動時してもデータの変更内容を保存しておかないといけないことにサポートチャットで気付いた
    • Redis に入れてるデータが消えないようにするスクリプトを作成するなどの作業
    • 永続化のノウハウが無さすぎた
  • ここら辺で /report のレスポンスを高速化する必要がないことに気付く
  • アプリケーション側は Redis データ永続化とアプリ変更の2人に分かれた
    1. 永続化担当には MySQL のデータ(初期データ)を Redis に持っていくところもやってもらう
    2. コード変更側は永続化と初期データが Redis に入っているのを前提で、アプリコードの SQL を駆逐する
    3. 初期データが入っていないので /report のチェックが落ちる
    4. コードが合ってるのか間違ってるのか分からないまま進めたら、1 と 2 を統合しても案の定 /report のチェックが落ちる
    5. Redis に入れた初期データが間違ってるのかコードが間違ってるのかわからなくなってパニクる
    6. あきらめムード

後半

  • 16 時すぎくらい
  • 変更済みコードではデバッグが厳しいとわかったので app.rb をまっさらにして、小さい部分から変更していく
    • 決断が遅かった
  • 残りの時間ないでできることはギリギリまでやって終了

時間があればできたと思うもの

  • 主にアプリケーション周り
    • 一番最初に考えていた、SQLを駆逐した状態のコードにする
    • login_log は INSERT 結果を待たずにすぐ返す
    • attempt_login で banned, locked の処理は並列化できる(並列化のコストがありそう)
    • KVS を Ruby の Hash にしたり memcached にしたりの検証
    • @lesamoureuses さんは Varnish 周りや my.cnf 、カーネルパラメータ周りをやりたかったみたい
  • SQL を駆逐するつもりしかなかったのでインデックスを張ってない
    • 最初にアプリケーション全体に手を加えるという博打をやらずに、インデックス張ったりに手を出してもよかった

知見・反省点

  • 中盤まで /report のことしか考えてなかったので、レギュレーションをちゃんと読む。もはやみんなで音読するレベル
  • 仕事のやり方みたいな話だが、大きな変更をするときは小さい仕事に分割してちょっとずつ進める
    • 実際に3人で本番を想定して練習することがなく、役割分担が不慣れだったということもある
    • 練習が足りない
  • 前日に Varnish とか Redis を触りだすレベルだったので、もっと持ちネタを増やして望む