ISUCON 6 予選通過しました

ISUCON 6 にチーム「morimoto組」で参加して、予選を通過して決勝進出することになりました。

ISUCONは過去5回のうち優勝3回、3位1回、出題1回、ということでもう引退(勝ち逃げ)しようかな…とも思ったのですが、今年は出題にも関わっていないので参加しないと完全に縁が切れてしまうし、それも寂しい。ということで。

チームメンバーは直前まで決まらなかったのですが、結局会社の新卒1,2年目( id:amusan , id:moshisora ) と組むことにしました。若いとはいえ去年と今年の社内ISUCON優勝メンバーです。(歳の差何歳だろう)

当日やったこと

天気は悪いが見晴らしはいい会議室を確保して万全の体制 (まぶしいのですぐブラインドは下ろされました)。

言語は Perl です。

  • インスタンス起動、事前に用意していたChefを実行して環境整備
  • isutar を isuda 内に取り込み
  • 正規表現の生成を Regexp::Trie に変更
    • 各プロセスで生成すると重いのと整合性を確保しづらいので、POST /(initialize|keyword) を処理するアプリケーションを1プロセスだけ起動するようにして分離。生成された正規表現を Redis に保存し、htmlify を処理するプロセスはその正規表現を使う
    • 1プロセスのみにしたので、keywordがpostされてきた順番に $trie->add($keyword) していくだけで整合性が維持できる
  • 静的ファイルは nginx で static_gzip、expires max を付与する
    • 304が返せるようになる

nginx.confはこんなかんじ。

 http {
    upstream app {
       server unix:/var/tmp/app.sock;
    }
    upstream app_post {
       server unix:/var/tmp/app_post.sock;
    }
    include /etc/nginx/mime.types;
    server {
        location ~ ^/(css|img|js|favicon\.ico) {
           gzip_static on;
           expires max;
           access_log off;
           root /home/isucon/webapp/public;
        }
        location /initialize {
            proxy_pass http://app_post;
        }
        location /keyword {
            set $app "app";
            if ($request_method = "POST") {
              set $app "app_post";
            }
            proxy_pass http://$app;
        }
        location / {
            proxy_pass http://app;
        }
  }
}

ここまでやった状態で14:40すぎに8万点ほどでトップに躍り出る。

ここで、アプリケーションのプロセスを再起動せずにベンチを回していくと、どんどんスコアが上がっていくという現象を確認。2回目だと12万点ぐらい。 特にメモリ内に cache は作っていない (つもりだった) ので、何が原因なのかさっぱり分からず。

ここから、他のチームがスコアを上げてきたのになかなか上げられず苦しむ時間帯。

  • / でoffsetが大きいクエリをentry using(id) の自己結合に
  • /stars を Resis にキャッシュ (get_multiで一気に取る)
  • uri_forを消す
  • user_nameもセッションに入れてクエリを消す
  • isupam は何とかできないかと考えたが結局手を入れられず

これでだいたい9万ぐらいだったので、このままだと予選通過はボーダー付近だった模様。

17:30ごろから2度目のベンチでスコアが上がる現象を解明するべく、個別にプロセスを再起動して切り分けしたところ、POSTを担当するプロセスを再起動しなければ速度が出る、ということを確認。こいつは Regexp::Trie のオブジェクトを永続化して持っているので、二回目は既に初回のベンチでキーワードが追加済みの状態になっていた (ので速かった)。

つまりこの状態では本来リンクになってはいないはずのキーワードがリンクになる状態。アプリケーションとしてはまずいんだけども、ベンチマーカーがそれを検出しても結果がPASSとされていればスコアも有効である、ということを運営とのやりとりで確認していたので、(多少良心が痛むものの) 突っ込むことに。

1回ベンチが走って追加された状態のキーワードをテキストファイルに書き出しておいて、initialize でそれを Regexp::Trie にaddしておくというコードを追加して、終了1分前にベンチqueue投入。18時の終了3秒前ぐらいにベンチが始まり、その状態で dstat をみていた限りでは14万点だしたときとおなじぐらい捌けてそう、というところで(最終的にスコアは知れずに) 競技終了。

結果、143,366で通過できました。

予選を終えて

チームメイト二人ともなぜか Perl-5.24 を手元で動かせずに、手元でコードの確認ができない状態だったのが完全に想定外 (環境構築で30分ぐらいハマってたので「もうそれは諦めて、こっちで動かすので」という判断を早めにできたのがよかった…)。それ以外は、結構ちゃんと戦えたのかな、という感想です。

聞くところによると、htmlfy()の結果cacheは結構雑でも (リンク関係を検出はされるものの) スコアが出せたようなので、ここがもっと厳密だったら展開も変わってきたのかなあ、という印象はあります。(とはいえ、一発アウトにするのは出題側も誤検知が怖いんですよね…)

幸いにも、これで ISUCON 本選は6年連続で出場できるので、精一杯戦いたいと思います。運営の皆様、楽しいイベントを本当にありがとうございます!