tech.kayac.com

技術部新卒研修担当の fujiwara です。

前回の記事「2013年の新卒研修と社内ISUCONやりました - (1) 研修編」に引き続き、新卒研修の最後を飾るイベント、社内ISUCONについて詳しく振り返ります。

社内ISUCONとは

レギュレーションはこちらです。

  • 各チーム1台ずつ使用できる仮想マシン上で、お題のアプリケーションを動作させる
  • 外部からベンチマークを行って処理できたリクエスト数をスコアとする
  • アプリケーション、OS、ミドルウェアなど、どのようなチューニングを行ってもよい
  • ベンチマークスクリプトはデータの整合性をチェックするロジックが組み込まれており、アプリケーションとして不整合を起こしていることを検出するとFAIL(スコアなし)
  • 10:00〜17:00 までの作業中には適宜ベンチマークを実行できる
  • 作業終了後の最終計測でのスコアが高いものが優勝 (FAILしたら失格。17:00以前までのスコアは考慮しない)

参加したのは2013新卒8名が2名ずつで4チーム、先輩は10名を5チームに編成し、合計9チームです。

お題のアプリケーションはいわゆる nopaste (gistのようなもの) です。ユーザ登録、ログイン機能を備えており、投稿と★を付ける機能はログインした状態でないと使えません。サイドバーには最新投稿が100件まで並んでいます。

nopaste.png

ソースコードは一式公開しています

ログイン機能があるため、セッション管理が必要になるところが過去2回の本家 ISUCON とは異なるところでしょうか。

新卒チームは昨日まで実際に作成していたアプリケーションをベースに作業できる上、実行するサーバ環境も研修中の実習で使用していたもののため、先輩チームよりは内部の把握に有利です。これはハンデですね。

死闘の始まり

isucon-opening.jpg

10:00 から本家ばりのオープニングムービーに引き続きレギュレーション説明が行われ、各チームは作業を開始します。

1チームのみ、運営の不手際で仮想マシンに30分ほどログインできない事案がありましたが、これは優勝候補の一角「チーム kameda (handlename, kenjiskywalker)」だったのでハンデということでひとつ勘弁していただきたく…

ベンチマークは CIツール Ukigumo (に少し手を入れたもの) で実行し、スコアを一覧して表示できるようにしていました。

isucon-ukigumo.png

新卒の一撃

競技開始から2時間ほど経過した12時過ぎ、新卒チーム ganbaru (MacoTasu, ichinose) がここまでの最高スコア 13,000 over を叩き出し、先輩チームに動揺が走ります。初期スコアは200程度ですので、この時点で60倍以上の高速化をしたことになります。

社内isuconを終えて - Maco_Tasuの技術ブログによると、この時点でサイドバーで大量に飛ぶクエリの書き換えとindex追加、nginxで静的ファイルをさばくなどのチューニングが行われていたようです。

デッドヒート

なかなかどのチームもブレイクスルーを起こせないまま、しかし着実に先輩チームはスコアを伸ばしていきます。

チーム beer (soh335, Konboi) が 15時前に 20,000 に到達、同じく先輩のチーム kameda と fugu (macopy, 9re) がそれを追いかけます。

しかし午前中に会場の空気を震撼させた ganbaru が 16:25 に 23,000 超えでトップを奪還、激しいデッドヒートを繰り広げる展開となりました。

部長、動く

終了まであと1時間を切る激闘のさなか、2年目のtkuchikiとコンビを組み、あえて自分はコードを書かずにサポートに徹していた部長 syoji がここで満を持して動きます。

isucon-irc.png

しかし詰んだ。

終演

17:00 の競技終了直前、どのチームも作業をしてはベンチマークを繰り返し、計測ホストでは最大4並列でベンチマークが実行され、5分間の平均トラフィックは500Mbpsを超えました。

ここまでトップを争っていたチーム beer は終了直前に FAIL を連発し、焦りを隠せません。対して新卒の ganbaru は最後 16:55 に 22,774 を記録して首位に立ち、そのまま計測終了。

これはまさかの新卒チームの勝利か。入社2週間の新卒に叩きのめされた先輩の威厳はどうなる。

最終計測

競技終了後、最終計測に入ります。

公平を期すため全マシンを一回再起動。運営側がDBを初期データにリセットし、その後各チームが初期化の手作業を行うことはできますが、ベンチマークは実行できません。

そして、競技中のスコアは、最終計測には全く加味されません。

これまでどれだけ FAIL していても、最終計測で成功してスコアを残せばそれが採用。ここまでどれだけ中間スコアが高くても、最終計測で FAIL したら失格、スコアなし。それが ISUCON です。

計測中には、運営側からどんなチューニングをやった?というアンケートなどを行っていました。

結果発表

優勝 team: beer (SCORE 25,133.4)

終了直前にFAIL連発して危ぶまれていた beer でしたが、最終計測を見事にpassして優勝です!

一方、勝利を8割方手中にしたと思われた ganbaru はFAILで失格。2013新卒チームは残念ながら全チームが FAIL でスコアなしという結果に終わりました。

あとから聞くところによると ganbaru はデータリセット後の初期化処理をミスしたそうですが、本番でミスをしないのも仕事をする上での実力のうちですね。

最終結果は以下のようになりました。先輩チームは最終的にはきっちりスコアを出して上位に食い込むあたり、さすがに実戦で鍛えられた力を感じます。

   team     | status  |  score     | members
------------|---------|------------|----------------------------------
beer        | SUCCESS | 25,133.4   | soh335 Konboi
kameda      | SUCCESS | 24,125.33  | handlename kenjiskywalker
fugu        | SUCCESS | 21,768.46  | macopy 9re
syoji       | SUCCESS |  7,623.68  | tkuchiki syoji
ganbaru *   | FAIL    | 19,914.95  | MacoTasu ichinose
ky          | FAIL    |  9,078.29  | tei-you chen-xiao
p_shake *   | FAIL    |  8,806.17  | keishake p_chin
chinatown * | FAIL    |    687.39  | m0t0k1ch1 piyon
gumma *     | FAIL    |    616.7   | gs hosono
* は2013新卒チーム

以下参考記録
------------|---------|------------|----------------------------------
reference   | SUCCESS | 53,977.37  |
acidlemon   | SUCCESS | 24,235.15  |

講評

今回の社内 ISUCON の出題では、最初に提供されるリファレンス実装には意図的にかなり無駄な処理が含まれています。研修の一環ということもあり、特に罠らしい罠は仕掛けていません。初期スコアは200 (1分間のHTTPリクエスト数) 程度ですが、

  • DBに適切にindex追加
  • サイドバーで100回のループ中に都度発行されるクエリを JOIN に書き換える

だけで 13,000 程度のスコアに到達できるような出題になっています。 つまり、ここまで到達できればほぼ合格点、という水準がスコア 13,000 以上ということになります。

優勝争いはそのあたりを確実にクリアした上で、サイドバーのレンダリングが重いのを部分的に cache するなどの方策を適切に入れることができるか、というのが焦点になったようです。

優勝チームのblogエントリは以下です。おめでとうございます!

(おまけ) 優勝チームの2倍のスコアを出す方法

出題者(fujiwara)はこのアプリを火曜日の夜中に書き、水曜にテストを書き、木曜にベンチマーククライアントを書いて金曜に本戦を行った関係で、どこまでスコアを伸ばせるかを事前に追い込むことができていませんでした。

ベンチマーククライアント側の限界を確認するために、nginx で return 200 するだけのサーバに対して負荷を掛けて、最高100,000程度まで到達できることを確認したのみです。

ということで、競技中に自分もリファレンス実装からどこまでスコアを伸ばせるかチャレンジしていました。

結果、優勝チームの約2倍、53,000のスコアを記録することができましたが、これはベンチマーク計測でどのような整合性チェックをしているか(=どのようにすればチェックをかいくぐれるか) を把握していたからできた手法という感があります。

チェッカーはログインした上で投稿や★をPOSTし、その結果がサイドバーなどに1秒後に反映されているかどうか見てくるのですが、ログインしていないで単に負荷を掛けるだけのクライアントではそのチェックが緩い、という特性を利用します。

具体的には、varnish をフロントに置き、以下のような設定をしました。

  • GET 以外は application に pipe (キャッシュしないでそのまま返す)
  • Cookieに plack_session=[a-f0-9] が含まれている場合 (ログインしてセッションが維持されている) は application に pipe
  • それ以外は静的ファイルまたは application へ lookup (キャッシュする)

しかし、キャッシュ可能な画面でもアプリケーションは Cookie を発行してそれをベンチマーククライアントが維持してしまい、結果 Cookie の有無だけではログイン状態を判別できないという(出題意図にない) 実装が競技中に判明してしまったため、急遽必要ない Set-Cookie ヘッダを削除して Cache 可能にする Plack::Middleware を書くことになりました。

スコアが出せたのは終了13分前。出題者としては優勝チームのスコアに負けるわけにはいかず、相当ギリギリの戦いでした…

ベンチマーククライアントと動かしかたについても公開しています ので参考まで。

まとめ

新卒研修の最後に2013新卒 vs 2012新卒でバトルしたらおもしろいんじゃない?というぐらいのネタで開催することになった社内 ISUCON ですが、参加者の誰に聞いても楽しい(ただしFAILしたチームは悔しい) という、非常に充実したイベントになりました。

普段の仕事ではいろんなしがらみがあって同一条件で競うことはなかなかできないわけですが、そういう事情を一切排除して、公平な条件でガチでぶつかり合うというのはとても楽しいことですね。

みなさんも是非開催してみると想像以上に面白いのでは、と思います。

# ただし、出題者はかなり胃が痛い思いをする可能性がありますのでお気を付けください