ISUCON3 を開催しました

参加者の皆様、共催で運営となった LINE, DataHotel, カヤック各社の皆様、本当にありがとうございました。いくつかトラブルがあったものの、本選もなんとか無事に終えることができました。

まずは優勝した LINE 選抜チームの皆様、おめでとうございます!なかなか初期スコアから上がってこないので内心ものすごく心配していましたが、ポイントを見極めて作業が終わったところで一気にスコアを上げてきたのは感服しました。

本選終了から48時間経過したいまでも頭の疲労が回復しきっていない感じで、整理できていないので思うままにつらつら書きます。

以下長文になってしまうので最初に告知です。

ISUCON3反省会 というイベントを 11/15(金) に行います。ISUCON参加者でなくてもどなたでも無料でプレミアムモルツ飲み放題ですので、日時が迫っていますが是非お越しください。

出題内容について

お題は以下のような感じです。

  • 画像版のTwitterみたいなリアルタイム投稿アプリ
  • 投稿の公開レベルを、パブリック、フォロワーのみ、プライベートで指定可能。閲覧権限がないユーザの画像ファイルへのアクセスには 404 を返す
  • HTTP long polling でのタイムライン取得機能あり

『予選とがらっと変えてきた』的な反応が多かったのですが、これは実は順序が逆でして。ISUCON3出題の裏側 | tech.kayac.com - KAYAC engineers' blog で書かれているとおり、最初に出題内容を考えたときには本選のお題を決めたんですね。

画像投稿で、リアルタイム通知があって、複数台ちゃんと分散しないと勝てないようにしよう、という方向でした。

予選をやるとなると、本選のネタバレに繋がる内容を盛り込むわけにはいかず、必然的に全然違うタイプの出題になった、ということなのです。

リアルタイム通知によって同時に画像変換にリクエストが殺到する、というネタは、弊社の提供しているチャットサービス Lobi で実際にあった現象を元にしています。

実運用を考えるとリサイズ済みの画像をあらかじめ持つのは容量とファイル数の問題で避けたい、けれども参照時に変換すると殺到問題がある、というあるあるネタですね。

『罠』について

初回の ISUCON 1 での通称「kazeburoの罠」の印象があまりに鮮烈なため、ISUCONといえば『罠』的な印象があるのですが、実は ISUCON 2 には特に罠らしい罠はないのですよね。

今回の予選では意図的な罠ネタを入れすぎてしまったのですが、これはおそらく出題したことのある人にしか分からない心理がありまして…

出題しているとお題についてあまりに熟知してしまうために、この程度の問題ではあっさり解かれてしまうのではないか、という恐怖心が時間を追うごとにどんどん増してしまうのです。その恐怖心に負けるとああなってしまう。

…というのを予選後かなり反省しまして、予選を通ってきた強者たちにはまっすぐな出題で応えるべきと思い直したため、今回の本選には特に意図して仕掛けた『罠』はありません。

意図せず罠的な挙動をしていた事象としては、

  • PerlでImagerを使うと画像差分が大きくなりがち (特に小さいサイズやアイコン)
    • これは出題中に image diff の閾値を決めるときに気がついていました
    • 実際に変換した画像をリファレンス実装のものと並べてみると、デフォルトだと1ピクセルずつ右下にずれたように見えます

というのがあります。画像変換のライブラリによって結果に差分がでるのは当然のことなので、差分を検出するツールを提供すれば問題なかろう、という判断でした。

あとは1MBを超えるサイズのデータで nginx で 413 がでるとか、memcachedに入らないとか、そういう現象もありました。これも出題時に当然気がついていたのですが、昨今のスマフォは1MB以上のJPEG画像を平気で撮影できますし、仕方ないですよね。
(ただし、全画像を大きくすると100Mbpsの帯域に対するインパクトが大きすぎるため、1MBを超えるのは全体の数%程度です)

配点について

POST /entry とタイムラインへの反映については、レスポンス速度によって加点があります。

score = log(10 / $response_time) / log(2)

なぜ log(2) で割っているかというと、ベンチマーククライアントのタイムアウトが5秒なので、5秒ちょうどで1点になるようにしたかったためです。

10ms で返せれば約10点になるのですが、1秒で処理した場合との差分が7点弱。通常のGETが1リクエスト1点なので、あまりそこで速度を上げるのを頑張らずにGETを捌くので高得点が狙えるのは優勝チームが示したとおりです。

これは出題の罠といえば罠でしょうか。事前検証ではPOST時には保存のみ、初回GETで変換という方策でもスコアを出すことはできているので、どちらを取るかで直接の有利不利はないと思います。

初回GET時に変換という戦略をとった場合、タイムラインが流れた瞬間に複数のクライアントが同一画像を GET に来るため、そこですべてに変換処理を走らせるとCPUの無駄遣いになりますし、workload を上げれば上げるほど効率が落ちるのでスコアを出すのが難しくなります。

これを回避するためには同一画像に対する変換が同時に複数走らないように、なんらかの方法で排他制御 (事前検証ではRedisを使用) をする必要があり、本選のように時間が限られた状況では実装しきるのが難しい、ということはありそうです。

ベンチマークツールについて

予選では同一ホストで動作させる関係上、ソースコードが読めてしまうと対応が容易になってしまうので Go を使用しましたが、本選では参加者の触れないサーバからベンチマークをするためその心配はない、ということで、自分が一番実装速度が出せる Perl で書かれています。
(ソースコード提供は数日中にはしますのでお待ちください)

リクエストを順次実行するタイプのプロセスには Furl、タイムラインを監視して同時アクセスを飛ばすタイプのプロセスには AnyEvent::HTTP を使用し、複数のタイプのプロセスを拙作の Parallel::Benchmark で束ねて実行するような作りになっています。

帯域制限は tc を wrap してくれる大変便利な qos-control で1ベンチマークホストに対する設定を吐き出してから、設定ファイルを加工して125ホスト分定義しました。

複数ホストに対するベンチマーク実行は、LVS で NAT 構成を取っています。参加チームごとに 8000 + チームID のポートを割り当てて ipvsadm で設定、そのポートに対してクライアントが接続する形を取りました。(そのため一部のエラーメッセージで謎のURLが見えてしまうことになり、混乱をしたかたもいたようで、申し訳ないです)

このあたりの詳細は、ISUCON3反省会 で紹介する予定ですので、ISUCON参加者に限らずぜひ無限プレモルしに来てください。お待ちしております。

さいごに

まだこれから結果まとめエントリを書いたり、ソースコードやAMI(?)公開をしたり、いろいろ作業は残っていますが、とりあえず今日の所はこれぐらいで。

来年のISUCONがどうなるのかはまだ誰も分かりません。が、是非誰か出題に名乗り出ていただきたいなあ、というのが正直な気持ちです。

いきなり言語実装を大量に揃えて、となると準備も大変ですから、まずは社内や身内で1言語でやってみるといろいろ面白いのではないかと思います。身内だけなら実装に不備があってもごめんなさいで済みますし。実際出題すると相当勉強になりますし。

今回は100万円の賞金が掛かっているがために、出題で下手を打ったら切腹もの、というのが最大のプレッシャーでした。でも当日競技を見ていて、最後のほうは100万円のことを忘れるぐらい面白かったですね!