ISUCON11の予選に参加した時の話をタイムラインベースでお話していきます。
2021/08/21に開催されたISUCON11の予選に参加してきました。
今回で3回目の参加になりましたISUCON11だったのですが、思う様な結果が残せず惨敗したので、恥をしのんで今後に活かすためにも当日の動きや対応や考えたことや失敗を残していきたいと思います。
チーム名: L&R
最終スコア: 19944 (220位/600チーム中)
ベンチを実行してた時の観測したかぎりの最高スコアは21000点くらいでした。
今回も例年のごとく会社の同期(今は元同期)のleedotdevと二人で参加してきました。
主な担当分けとしてはleedotdevがNginxを含むインフラの担当。僕がアプリケーション、DBを主に担当し、少しNginxチューニングするという感じで行っていました。
例年ですが、もともと二人の得意分野がうまいこと被らなかったので作業のカニバリとかはなくスムーズに作業を行うことができていました。
前日まで
当日朝9時
事前に準備していた開始後に行うことを二人で確認し、作戦を立てる
9:40~
開会式をyoutubeで見る。
10:00
役割分担をして準備実施
一旦ここで初期のベンチ実行
10:30
glancesを見るとCPUが常に100%に張り付いていてアプリケーション側の負荷が大きすぎたので、まずはアプリケーションのエンドポイントで負荷が高いことがわかる/api/conditionのリクエストのみを別のインスタンスに切り離した。 サーバーの使われ方はこの段階では以下の通りの構成
server1 main, app, mysql
server2 /api/condition
server3 放置
一旦slowqueryを見てわかりやすく改善できるところからということでindex戦略に走り、アプリケーションないの全てのクエリを抜き取って、問題のあるクエリに適応するようにindexを貼った。
CREATE INDEX idx_jia_isu_uuid ON isu_condition(jia_isu_uuid);
11:40
alpの結果で/api/trendと/api/conditionと/api/isuに問題があったので、まずは可能なところからということで/api/trendの改善を実施
13:30
14:40
サーバ-1に/api/isu/*/icon
と/api/isu/*/graph
の二つを向けるように変更
15:00
set :static_cache_control, [:public, max_age: 60 * 60 * 24 * 365]
17:00
/api/condition/:jia_isu_uuid
がボトルネックになっているのは明白なので、この処理を改善する17:30
18:00
/api/condition/:jia_isu_uuid
のSELECT文にアプリケーション側で指定しているデフォルトの取得件数分のLIMITをつける対応を行おうと試みる。
18:25
撤収作業開始
最後にベンチの実行
18:40
これは本当に反省している点でslow queryの結果を見て、以下の系統のクエリに問題があることを認識していたのですが、なぜかtimestampにindex貼っても効かないよなーとか謎の感想を抱きindexを貼ることを試さないでしまいました。これがのちの感想戦で初手一番効いたというのが話されていてものすごくショックを受けました。 timestampって効くんですね。しかも結構大幅な点数増加が見込めるものだったので、試してみるべきだった…。
SELECT * FROM `isu_condition` WHERE `jia_isu_uuid` = ? AND `timestamp` < ? AND ? <= `timestamp` ORDER BY `timestamp` DESC',
この処理がポイントの増加に大きく影響することがわかっていたのですが、対応方針が思い付かず対応することができませんでした。正しい対応方針はSELECT文でtimestampで取得する範囲を指定するのが正しい対応方法だったようですが、全然思い付かずかなり後で反省しました。
今回はRedisを利用することを予定していたが結局実行に移せていなかったです。導入ポイントが思いつかなかったのが最大の理由だけど、もしかしたらキャッシュさせることを前提にコードを読めば対応が変わっていた可能性もあるので、多角的視野でアプローチを考慮することが大切でしたね。
アプリケーション側に常に問題が残り続けCPUリソースを下げ切ることができなかったので、アプリケーション担当としてはかなり残念ポイントでした。かなり悔しい気持ちでいっぱいでしたね。
ここからはできなかったこととは別にISUCON11全体での反省ポイントを挙げていきます。
今回の大会は今までで一番準備ができていませんでした。チームメイトが母国に帰る&転職の準備を行っていたり、私自身も引越し作業をする必要があり、土日の時間に集まって練習するということができず、結局一週間前にちょっとだけ集まって準備ドキュメントの内容だけ確認するみたいな絶対的に練習時間が足りない状態で当日を迎えてしまったのがやはり影響し、最初のぐだり方は過去1でしたね。 やはり事前に過去問を解くことは重要です。次回は余念なく当日を迎えられる様に練習時間の確保をしたいですね。
なぜかいつもアプリの解析ツールの導入を忘れているんですが、やはり効率的にアプリケーションの弱点を見つけるためにもrubyであればstackprof等の導入は早めに検討すべきでした。
また、キャッシュ戦略に移るのがいつも遅くてやろうと思った時には時間ないなーってなってしまうので、システム解析ツールでメモリに余裕があればRedisやインメモリにキャッシュさせる方法でキャッシュさせることを検討すべきでしたね。
ソースコードを読まずにすぐにN+1問題だ!と飛び付いたのが大きな失敗でした。ここのソースコードは冷静に読むと最新の一件だけ返せばいいコードだったのに、冷静さを失ってしまって内容を確認しないままに実装を開始してしまって無駄な時間を使ってしまいました。改善の一番最初はソースコードの意味を理解するにあるなというさも当たり前なことを述べる失敗です。
結構よくあることなのですが、開始して直後にアプリケーションの仕様とレギュレーションは確認するものの実際に動くアプリケーションを触ってアプリケーションを理解するという作業をどうしても蔑ろにしてしまうことが多く、かなりその部分で後から苦しむ経験をしています。 やはり最初にはアプリケーションを触ってその特性を理解するのが重要だと再認識しました。
最後までアプリケーション側のCPU負荷を下げ切ることができず、ミドルウェア側の解決がメインでどうにかスコアを伸ばしましたが、やはり根本解決にはつながらず、対応仕切ることができなかったことに痛く実力不足を感じました。 ここは多角的視野を養いつつより多くのアプローチでの解決方法を自分のものにしていくため研鑽を積む必要があると感じました。
ISUCON運営の皆様本当に準備いただきありがとうございました。
今年も本当に多くの学びがあり、楽しく参加させていただくことができました。
また、いつも一緒に参加してくれてるチームメイトのleedotdevにも感謝しています。 初めて参加した社内でのISUCONイベント以来ずっと共に参加してくれる彼との時間を大切にしています。
来年も一緒に出られるといいな。
そして、ISUCONはやっぱり楽しい!!!