(追記あり)みなさんISUCON4お疲れ様でした。はじめに運営のLINEさん、出題のクックパッドさん、そしていつも大量のサーバを提供していただいているデータホテル改めテコラスさんありがとうございました。ピザとお寿司おいしかったです。今回はベンチマークの気持ちになってCache-Controlヘッダをつけることが出来たかどうかだけがブレイクスルーできるかどうかを左右するという潔い問題でした。結果は既に既報のとおりで上位2チームだけブレイクスルーし、3位以下はずーっとネットワーク帯域にめいっぱいデータを流し続けてドングリの背比べとなりました。1Gbpsの帯域を使い切るレベルまでアプリを高速化できると大体スコアが8000〜9000程度となるようで、最後の1時間までほぼ8000台に団子となっていましたが、Cache-Controlでブレイクスルーした2チームを除くと私の所属するfujiwara組の15000というのはそれの約2倍近くとなっており、逆にCache-Controlなしでどーやってそこまで行ったんだと気になる方もおられるかと思います。そんなわけで簡単にやったことを振り返っておきたいと思います。あ、「簡単に」って書きつつ例によって長いです。初動が大事コードとレギュレーションをよく読んで、初期実装からPerl実装に切り替えてローカルモードでベンチマークして約10000。リモートモードはまだベンチマークサーバが不安定だったので試さず、ざっくり眺めて何が必要かを洗い出すことと、ベンチマークがどういう負荷のかけ方をしているかを確認しました。入稿がおわったらあとずっとimpressionしかしてないこの時点でとりあえずベンチマーカーが一通り入稿しおわったらあと広告表示しかしてこないことを把握して、そこをいかによく回すかが勝負なんだーと確認しました。動画をRedisにいれるのをやめる/動画をアプリで返すのをやめるとりあえずこの時点でネットワーク負荷をみたところガッツリOutトラフィックが出ていたので、こりゃ今回も複数台で返すようにしないと帯域勝負で勝てないなということで、1台専用になってたアプリをよく読んで何を改造しないと複数台で動かせないかを確認しました。あと、この時点で1号機は1コア、2号機と3号機は2コアということがわかり、なんでやねん...という気持ちでいっぱいでした。それをredisかなんかに吐くようにするのは必須で、手はじめに使ってるモジュールでちょっと速くなりそうなところから手を入れました。具体的にはRedisをRedis::Fastにしたのと、JSONをJSON::XSにしたあたりですね。あと、Redisを1台にまとめて全部のサーバが1号機のRedisにアクセスするように変更しました。アップロードされてきた5Mの動画がRedisに突っ込まれていたので、さすがにこれはねーだろーということでローカルディスクに保存するように変更しています。この時点では1台構成なのでローカルディスクに保存してnginxでそれをserveするだけでOKです。Rangeヘッダとかはnginxがよしなにやってくれるはず。動画をnginxで返すようにしてローカルベンチマークをすると大体スコアが16000くらい。ここでよーやっとリモートからのベンチマークがまともに使えるようになり、さっそくやってみると、えーとスコアが4100...?dstatで確認してみると、ローカルベンチマークでは3台のサーバがほぼ青天井に近い速度で通信できますが、リモートベンチマークではそこまでスピードがでていません。VMの乗ってる物理マシンが与えられた3台では物理的に一緒だがベンチマークサーバーは別サーバーということで、物理的な制約でリミッターに当たっているようだと推測しました。複数台構成できるようにするねってことで、アプリを書き換えてTSVでログをファイルに書き出しているところをRedisのLISTに格納するように変更。30分くらいでサクッと書き換えて(珍しく)なんのバグも出ずに複数台構成が完了。proxyでfallbackするnginxの設定を書いてそれで捌くようにしました。ローカルモードでベンチマークをかけて17000くらい。あれ思ったよりも上がらないな...。迷走の始まりさて、この頃にはリモートベンチマークツールのベンチマークジョブが4チーム並列となり、スコアが安定しなくなります。うーんそうはいってもローカルベンチマークはめっちゃ安定して17000だしなぁ...ということで、いろいろなバリエーションを試しました。ローカルIPでかけてみるグローバルIPでかけてみるworkloadをあげてみる複数台をやめて1台だけでやってみるなどなど。結局の所スコアが非常にばらついて、300〜7000でばらついたのでこりゃわからんなという感じに。なんとなく傾向としては「グローバルIP側からリモートベンチをかけるとスコアが落ちる」「複数台にしてfallbackしてるとスコアがおちる」ような感じなので、そこをなんとかする方向で考えます。ここでfujiwaraがアプリの返すJSONを見て、JSONの中にassetのURLが入っているのを発見しました。Redisに保存している広告の情報にどこのサーバに格納しているかの情報を付加して、uri_forを使わずにそのサーバに行くように仕向けるということで対処をすることになり、handlenameが実装に着手しました。5000を打ち込んだのですが、ベンチマークサーバからはMTUは1500だからあきらめろという感じのエラーが返ってきて「にゃーん」となったりしてました。その他にやったのは、nginxでlimit_rateをかけて動画の転送速度を制限してみるなどもやったんですが、limit_rateはIPごとの制限になるので意味が無さそうということと、そもそも動画を転送しないとリクエストがどんどん押し寄せて飽和して結局ダメということがわかっています。Hackを考える時間どうしようもなくなってきたのと、リモートベンチマークが帯域をちゃんと使い切ってくれない(ゆらぎ/ブレがある)問題でなかなか解くのが進まないのとでじりじりする時間帯。workloadを調整してみてもスコアは変わらず、相変わらず安定しないのでHackすることを考え始めました。ぼくが考えたのはスロット全部配信したらもう5倍確定だし、それ以降の動画は配信しない(めっちゃ遅く返す)というやつです。これをやるにはアプリが動画を返さなければならないので、えーそれはなーみたいな感じで躊躇して結局やりませんでした。そんなことを考えていると17時すぎにいきなり1位スコアが333555点に。えーなんだそれとなり、5Mのファイルをまともに配信してたら絶対でないスコアなので、やっきになって秘孔探しを始めます。"みたいな感じで動画じゃないものを返すというのをやってみました。リクエスト数の分布をみると、動画は全体の15〜20%程度なので、これが全部ERRORになってもFATAL判定じゃなければ他のAPIがガンガン回ってそれなりのスコアはでるはず!しかし現実はそんなに甘くありませんでした。動画を見に来るgoroutineはasset→count→redirectの順にアクセスするのですが、assetの検証に失敗するとそこでルーティーンがabortしてしまい、count/redirectが呼ばれないのでエラー率が25%を越えてしまうのです。考えたのですが、結局ちょっとでも動画を返すとたちまち帯域は埋まってしまうことが予想できたため、そこを深追いするのはやめました。その他にも、動画配信ラッシュがはじまらないように出稿速度を律速するのを狙ってupload_limit_rateをかけるとか、アップロードするファイルの受け取りをAppではなくnginxにしてみるとかのアイディアも出ましたが、結局いろいろ問題が予見されたのでやりませんでした。しているうちに30万点のスコアは更新されて7000点くらいまで落ち、なんだ再現性のないスコアか、ベンチマークツールのバグだったのかなー、忘れようとなりました。生ハム原木は30万点スコアをみてずっとそのスコアの再現に取り組んでたらしいので、そこで差がでてしまった...という感じです。1台構成に戻すさて、ここまででどうやら3台構成だろうが1台構成だろうが、ベンチマーカー側の帯域が1Gbpsで埋まっていて受け側が複数台いてもどうしようもなさそうということが見えてきました。ここで、Idobataのサポートチャットでまた3台のスペック違いについて質問がでていて、かたくなに「お答えできません」と回答しているのを見たので、こりゃCPUが1個しかついてない1号機になにかあるのでは...となりました。邪推して、CPUも余ってることだし思い切って1号機だけで捌いてみるというのはどうかというのを提案してやってみることにしました。すると、帯域は1Gbpsまでしかいかなかったのですが通信の安定度が2号機3号機とは全然違い、非常に快適に帯域が全部埋まりました。これが意図したモノなのかどうなのかは今でもわかってないですが、これなら1台構成で1号機にかけるのが一番スコア安定するねーということになりました。いえ、リモートベンチマークのスコア的には9000前後です。15000のスコアになったのはここからの押しの一手がありまして、fujiwaraが18時半くらいにトイレから戻ってくるなり「ベンチマーク、1台にローカルIPとグローバルIPの両方からかけてみましょう」と。いうことでやってみると、10800程度のスコアになりました。うーんもうちょっと伸びるかなーと思ったのですが、このときでもリモートベンチマークは2チーム並列になっていて専有ではなく、10800を出したときの前半は帯域が1Gbpsも埋まってない状態でした。物理機でグローバル側とローカル側が1Gbpsで別々のNICになっていたという感じなんですかね。このあと、じゃあ3台構成でに6個のIPアドレスに向けてやってみようぜというのもやってみたのですがベンチが詰まって完走せず、1号機の1台構成でグローバル&ローカルIPのデュアル構成でベンチパラメータを提出。その結果、本番計測では2Gbps相当の帯域をだいたい使い切ったらしく、1Gbpsを使い切ったときのの約2倍のスコアで3位となりました。感想などいやーいかがだったでしょうか。今回の問題、Cache-Controlに気づけなくて本当に悔しいですねー。ちなみに私はCache-Controlという話が講評で出たときはやられたーというよりはどちらかというとマジかよーという感じでした。広告って不特定多数に配信するので1分程度の試行だとCache-Controlが効くほどユーザの数に対して配信しきれるのかなーと。ベンチマークツールが実際のブラウザを模しているとはいえ、実際の広告でCache-Controlが効くのってその広告を一度みたユーザだと思います。今回ベンチマークツールが送ってくるUAは30個くらいあって、そのUAごとにCache-Controlが制御されてたんですかねー、というのが気になりました。UAごとじゃなくてベンチマーク全体でのCache-Controlだと、たぶん10〜20秒くらいで出稿された動画を全部配信し終わるので、そのあとが全力ぶん回し勝負だと60万みたいなスコアがでてもおかしくないです。UAごとにCache-Controlを判定していればだと広告150本に対して30ユーザいると4500通りなので、UAに偏りがない限り同じUAが同じ動画を引くことはそれほどなかったのかなーというのがなんとなーくモヤモヤと残りました。Cache-Controlの件、朝起きてまだモヤモヤしてたのでもうちょっと考えてみました。9まぁそんなわけで、まず動画を各ユーザに逐一配る発想をしてる時点でダメで、ベンチマークがかなり賢いというのはブラウザを模しているというよりもPOSTをパススルーして、なおかつCache-ControlつけてるときだけキャッシュするというCDNを模して作られていたという感じで考えるべきだったんですかね。考えるとまぁまぁ納得かな...。no-cacheがなかったのはベンチがCache-Controlないやつはキャッシュしないと言う実装になってるからつける必要がなかったのと、たぶんCache-Controlでコントロールできることに気付かせないためというところですかね。結局の所結論は変わらず、「我々はやられてしまった」ということです。acidlemonです。鎌倉市在住の普通のITエンジニアです。30年弱住んだ北海道を離れ、鎌倉でまったりぽわぽわしています。