ISUCON10予選に参加しました

こんにちは。飲食領域でテックリードをしている浅野です。
業務外ではあるのですが、9/12に開催されたISUCON10予選に参加させてもらっていました。

結果は初歩的なミスにより予選落ちとなってしまったのですが、一応参考スコアとしてはトップを記録していたので参考までにやったことの共有と、今後同じようなミスをするチームが出てこないよう啓蒙を兼ねて記事にさせていただきます。

やらかしの部分だけ知りたい方は結果まで飛ばしてください。

ISUCONについて

Iikanjini Speed Up Contest の略で、LINE社主催で開催されているWebアプリケーションのチューニングを競うコンテストです。
業界ではもはや説明不要と言っても良いレベルで認知されているイベントですが、今年は予選が1日開催となり参加枠に制限が設けられる中で数日で枠が埋まるなど、今の時勢の中でも衰えない盛り上がりと人気の高さがうかがえました。

また、今回はリクルートも問題作成に協力しており、ISUUMOというどこかで聞いたことがあるオリジナリティ溢れる不動産サービスがテーマになるなど、会社としても注目度の高いイベントとなりました。
※ 念のため、当然ですが私は問題作成には1㎜も関わっていません。

自分自身、ISUCONにはISUCON5から参加しており、社内ISUCONの運営に携わったこともあったりと非常に思い入れが強いイベントになっています。

また、前回のISUCON9では初めて本戦に出場することができたのですが結果が奮わず、今回はそのリベンジのために普段以上に気合いを入れて臨んでいました。

なお、前回リクルートから本戦に出場したチームは私たち theorem の他に2チームいたのですが

  • isucon_friends → 今回の問題作成担当
  • ふんばり温泉チーム → 今回も本戦出場

とそれぞれ継続して成果を残しており、正直若干凹んでいます。

当日の動き

さて、本題です。

大まかに取り組んだ内容を解説できればと思いますが、当日利用していたリポジトリを公開しているので、詳細が気になる方はそちらも参照ください。

なお、気合を入れて臨んだと書いておきつつ事前準備的なことは正直ほとんどしておらず

  • 利用するコミュニケーションツールの選定
    ※ 会話はGoogle Meetで、ホワイトボードツールとしてmiroの利用も考えてましたが結局出番はありませんでした
  • 情報共有用のスプレッドシートの用意
  • 序盤の時間の使い方の確認

くらいで、「後は流れで」という感じで当日を迎えました。

回数をこなした上での慣れと私たちのチームがそれで馴染んでいるというのもありますが、ISUCONは最終的にはその場での閃きとアドリブがものをいうので、あまり事前に詰めすぎても良くないんじゃないか、というのが私の所感です。

12:20 - 14:00

開始から1時間半くらいはコードには一切手を加えず、計測ツールの準備や構成の確認、サービスの仕様把握やコードリーディングを行いました。

実際にどこから手をつけるかの判断は当然計測の上でやっていくことにはなるものの、この時間内で「ボトルネックになりそうなポイント」をいかに洗い出せるかでこの後の作業をスムーズに進められるかが大きく変わるので、個人的には非常に重要な作業だと考えています。

スプレッドシート 当日実際に書き留めていた内容

また、今回利用したツールとしては以下になります。

主にはベンチ実行後、alp + pt-query-digestの結果をNginxのドキュメントルート配下にhtml出力し、そのURLをSlack通知するという方法で確認できるようにしていました。
この形式だと結果を中途半端にSlack通知するより視認性も高く、後から見返す時もわかりやすいのでオススメです。

特に今回は最初から最後までDBがボトルネックとなっていたので、pprofなどの出番もなく、ほぼこの結果のみからボトルネック予測と対応優先度の判断を行っていました。

14:00 - 17:30

14時頃から本格的にコードを触り始めます。

初期状態で頭ひとつ抜けてボトルネックになっていたのは /estate/search /chair/search でしたが、抜本的な修正は規模が大きくなりそうだったのである程度効果がありそうなindex追加などを行ってお茶を濁しつつ、次点で時間のかかっていそうな /estate/nazotte の改善に手をつけることに。

「空間インデックスが使えそう」というアタリはつけていたのですが、具体的な実装については誰も知見がない状態でした。
そんな中、チームメイトが手持ちの書籍からMySQL5.7でInnoDBでも利用できるという情報を見つけ、Generated Columnsを利用することでデータ移行などもすることなく想像以上にサクッと対応が完了しました。

17:30 - 20:00

この辺りからBotのリクエストが目立つようになってきたため、弾くように
アプリのCPUネックがなさそうだったので深く考えずNginxではなくアプリ内のロジックで実装しましたが、この選択もあり今回結局Nginx周りはほぼノータッチでした。

1
2
3
4
5
6
7
8
9
10
11
1346 Isupider-b760093c-99c7-4f8a-8db6-8bdf7bfe142d
1344 isubot-c596cc16-f90a-43e0-8f74-ef757c22dfe9
1324 57a0a97e-c58d-4b40-999c-015f60d5fcc6-ISUCONCoffee
1314 f1abca3c-dff4-42dd-9a12-17f83c92840c-ISUCONCoffee
1302 Isupider-6c7ca67a-b1c9-4e38-89c7-ace876252ec7
1176 ISUCONbot-aa2b1c23-222c-41ed-b6fe-6a64d7e0050b
1128 Isupider+4fc89804-4887-44d1-83f7-943b486ff5e0
1116 Isupider-d0ecbccf-0067-4776-87b5-3ca2fa9f89d8
1106 e1ac2112-6753-4b74-bfde-0e7ccb447276-ISUCONCoffee
1086 ISUCONbot-Image/368d3470-e815-431e-81e0-95e6358d8d69
 920 ISUCON Web Browser mobile-eb2b06e2-c5b9-42e3-9cd7-876644f3e754

この時のUA分布

その後も検索リクエストが変わらず圧倒的な時間を占めているので、本格的に改善策を検討することに。

リクエストの内容を細かく見ていく中で

  • 同一リクエストパラメータでの検索が多い
  • 更新リクエストはあまり多くない
  • レスポンスサイズはせいぜい数十KB

ということがわかっていたので、インメモリキャッシュで丸っとレスポンスをキャッシュしてしまうという大味な対応を行ったのですが、これが比較的効果があり、キャッシュの対象を low_priced などにも広げていきました。
※ なお、レスポンスサイズからメモリが溢れないことは確認していましたが、実際にはLRUキャッシュなどを使うべきだったかとは思います。

その他

などの対応を行った結果、凍結前に一気にスコアを伸ばし、2位のチームにかなり差をつけてのトップにおどり出ることができました。

スコアボード

20:00 - 21:00

スコア凍結された時点でこれ以上無理してスコアを伸ばす必要はない状況だったので、着手していた chair.stock が0のレコードを別テーブル管理にする対応などは進めつつ、20:30頃には店じまい体制に入り、余分なサービス停止/ログの削除や、再起動試験の確認を行い、自信満々で最後は公式のYouTube配信を眺めながらタイムアップを迎えました。

正直「勝ったな」と思っていました。

……この時は。

結果

追試が進む中、運営の不穏な発言に一抹の不安を感じつつ、まだ自分たちの勝利を信じていました。

メッセージ

そしていよいよ結果発表。

最後まで theorem の名前が呼ばれることはなく、発表は終わりました。

メッセージ 発表終了直後の反応

再起動試験で問題ないことは確認していたはずなのに、何が足りなかったのか。

それは、 再起動後にベンチマークが走り切るかどうかしか確認しておらず、再起動直後にアプリが正常に動くかを確認できていなかった ことです。

実は、インメモリキャッシュの対応をする中で、キャッシュを保持するmapの初期化を /initialize でしか行っていませんでした。
つまり、 起動後にinitializeが走らないと動かない状態になっていた ということです。

あまりにも初歩的な実装ミス、かつレギュレーションに記載のある
ベンチマーク実行時にアプリケーションに書き込まれたデータは再起動後にも取得できること
の動作を確認する際には間違いなく発生するため、本来事前に気付けるはずのものでした。

それを、「データ永続化部分で特に凝った実装をしていないから確認するまでもないはず」と思い込んで確認を怠ったため、再起動試験で失格という最悪の結果に繋がってしまいました。

最後に

今回、失格になってしまったことというよりは、その原因となったやらかしが酷すぎたのでそもそも記事にするか葛藤があったのですが、これを読んだ方が同じ轍を踏まないよう、少しでもお役に立てば幸いです。

また、思い込みによるミス・確認漏れというのは実際のWebサービス開発でもつきものなので、「これがISUCONで良かった」とポジティブに捉え、また次回懲りずに参加させてもらおうと思います。

去年の雪辱の思いは同僚の ふんばり温泉チーム に託し、本戦での活躍に期待させてもらいます!