ISUCON7予選に秒速5000兆クエリというチームで参加した

ISUCON7 開催&日程決定! #isucon : ISUCON公式Blog

ISUCON7のオンライン予選にid:astjid:tanishiking24の3人で秒速5000兆クエリというチームで出場した。最終スコアは9万ちょっとで、ベストスコアとほぼ同値だったので実力を出し切ったと思う。

ISUCONに競技者として参加するのは初めてで、エンジニアリングにスコアをつけて競い合うというのは聞いていた以上に楽しい。

ちなみに「競技者として初」というのは、昨年のISUCON6の参考実装の移植として関わっていたので、ISUCONと関わったという意味では初ではなかった。

準備

2日前くらいにcatatuy/private-isuで練習した。振り返るとISUCON7の予選問題も画像をいかに効率よく返すかというところが最初のボトルネックで、早速予習が活きてお得。

pt-query-digestを使うとか、nginxのアクセスログを解析してボトルネックを見つけるとか、見つけたボトルネックの解消の分担だとか、タイムキープだとか、一通り押さえておきたいところは練習できたので事前準備は万端と言ってよい。

当日

開始が遅れるとのことだったのでオフィスの近くにあるちょっといい肉料理のお店でランチをいただいた。

肉専科はふう 京都御所南の和牛ステーキを堪能できる洋食のお店

開始してからは、まず3人でレギュレーションの読み合わせをした。予選だけど複数台構成ということでおおっとなる。練習していた時のデプロイは雑にホストに入って `git pull` するだけだったけど、手間とかリスクを考えて早めにデプロイ整備をしようということになった。

ほか、ローカルとグローバルにそれぞれ帯域制限があることなどを確認しあった。この時点で参考実装は読んでいなかったけれどグローバル側が100Mbpsだったので、とにかくベンチマーカーから送られてくるリクエストを減らす = クライアントキャッシュを使うのが肝要というあたりをつけた。

読み合わせが終わってからはid:astjSSH鍵のセットアップなどをしつつ、自分が初期プロビジョニング (mackerel-agentを入れたり、redis, memcachedを入れるなど) やデプロイ準備を進め、id:tanishiking24がkataribeのセットアップをしつつコードリーディングを進める、という分担になった。

レギュレーションを読み合わせましょう、とか、3人がどういう分担になるといいか、とかはISUCON本戦出場経験のあるid:astjに音頭を取ってもらった。普段からオペレーションも得意だしあてにはしていたけど、実際かなり頼りになり助かった。
id:tanishiking24も「じゃあこれよろしく」と振って危なげなく進めてくれるので、チームの並列度はマックスで最高だった。

練習でやったとおりnginxのアクセスログMySQLのslow.logを見てボトルネックを1つずつ潰していく方針で進めた。

id:astjがやってくれたimage.nameにuniqueインデックスを張る変更でスコアが5万くらいに上がってテンションが上がってきたと思う。早いチームは早いうちからスコアを伸ばしていて内心やや焦っていたけど、同じ土俵に立てたことで安心しつつ、競い合えているなーと楽しくなってきた。

その後、N+1クエリを殺すなど分担してスコアをちまちま上げつつ、いよいよアイコンの配信をどうにかしないと帯域で当たってどうしようもない、という状態になって案を考えはじめた。

last-modifiedをつけてconditional GETさせよう、という方針になったのだけど、これはあとから考えると穴がある判断だったし、ハマる原因になってしまったな、と反省。

JSやCSSにはif-modified-sinceは来ているように見えたけどアイコンには来ていないので、last-modifiedヘッダの日付形式がおかしいのではないか、とか、304を返すロジックを間違えているのではないか、とか後から考えれば的外れな検討ばかりして時間が消えていった。たぶん3時間くらい費してしまったと思う。

最後のほうは本当にお通夜で、しかも大差ない変更をしたつもりだけどスコアが5万→3万→2万→3万とよくわからない上下をするので、一層お通夜が進んだ。

だんだん頭がおかしくなってきて、gzip圧縮かけているとstrong etagが毎回変わってしまいそれでキャッシュされていないのでは? ということになりgzipを切るなどしたが、効果はなし。

頭も朦朧としてきてヤケクソになって誰ともなくcache-control max-age=0とかつけてfailしなかったら儲けものじゃない? って話になり、ベンチマーカーを走らせるとなんと6万くらいになった。

その時のid:astjはこんなかんじだった:

                                 ,.へ
  ___                             ム  i
 「 ヒ_i〉                            ゝ 〈
 ト ノ                           iニ(()
 i  {              ____           |  ヽ
 i  i           /__,  , ‐-\           i   }
 |   i         /(●)   ( ● )\       {、  λ
 ト-┤.      /    (__人__)    \    ,ノ  ̄ ,!
 i   ゝ、_     |     ´ ̄`       | ,. '´ハ   ,!
. ヽ、    `` 、,__\              /" \  ヽ/
   \ノ ノ   ハ ̄r/:::r―--―/::7   ノ    /
       ヽ.      ヽ::〈; . '::. :' |::/   /   ,. "
        `ー 、    \ヽ::. ;:::|/     r'"
     / ̄二二二二二二二二二二二二二二二二ヽ
     | 答 |     コ ロ ン ビ ア       │|
     \_二二二二二二二二二二二二二二二二ノ

あとは初期データに含まれるアイコン画像を静的に書き出すのをやって9万 (ベスト値) が出る。……ものの、my.cnfを書き換えてベンチマークを走らせると5万くらいに落ちる→もう一度走らせる→スコア戻らず→変更を戻す……というようなよくわからないスコアの上下が相変わらず発生するので、最後の30分はベストスコアである9万を越えるまでひたすらエンキューする猿たちになっていた。結果、最後の10分くらいでベストスコア近いスコアが出たので、そこで打ち止めとなり終了。

実践できて、次回もやっていきたいこと

  • ボトルネックの解析・発見→修正という基本的なプロセスを回しつづけたこと
  • 30分ごとに現況確認をしあい、ハマるのを概ね防げたこと
  • (良くも悪くも) 普段の自分たちが切れる手札だけを切れたこと、本番一発勝負の必殺技みたいなものはなかった

反省点

  • ベンチマーカーのconditional GETの挙動が不可解だとなったときに、動かす手が止まってしまった
    • 「仕様的には我々は間違っていないはずなのに」となったときに、仕様に照らすのではなく、ベンチマーカーの動きを知るためとにかくあれこれやってみるべきだった
    • 他にボトルネックのあたりはついていたが、帯域で当たっているのでまずアイコンをどうにかしないといけない、けど手がない……ということで3人とも止まってしまった
  • 他の人の感想を見るとgzip_staticとか知らないディレクティブがいろいろあった

感想

普段やっていることに近いし社内の他のチームと感想戦をしあってみて、丁寧な配信最適化をやろうというかんじで、楽しめたし自分たちに足りていないところがあることを痛感させられ、たいへん楽しめました。

悔しくありつつも、初ISUCON参戦で楽しかったので、次は優勝したい。

余談

予選参加することを決めたあとで10月22日に『響け!ユーフォニアム』のコンサートと被っていることに気がついて、慌てて21日の参加で問題ないか確認した時が一番肝が冷えた。