ISUCON初参加で学生2位を取りかけた話
はじめに
2024-12-08 に開催された ISUCON 14 にチーム最上川(@kawaemon, @shun-shobon, @re-taro)で参加しました。今回が ISUCON 初参加でした。本番では悔しくもデータ保持違反[1]で失格でしたが、スコア順位としては 37,127 点で全体 8 / 834 位、学生 2 / 99 位 でした。
また、その後 1 ヶ月程度に渡って開催された感想戦[2]においては、850,573 点で全体 2 位、学生 1 位でした。(感想戦については別記事にします)
この記事では、ISUCON に参加することになった経緯や、本戦中なにをやっていたかやその反省について書きます。
要約
本番中できたこと
- 各種 index
- 各種 config いじるだけ系秘伝のタレ
- mysql を別鯖に逃す
- ride_status のバルク取得
- nginx で静的ファイルのキャッシュ
- 総 ride 数、総評価、椅子の現在位置のキャッシュ
- 通知ポーリングレートを落として負荷削減
- 1回のマッチング処理で全てのライドをマッチング
- クーポン使用の最適化
感想
- なぜもっと早く準備しなかったのか
- とにかく再現性のある計測・試行を早く回せる仕組みを構築するのがいちばん大事
- 計測結果とコードだけ見ててもある程度までは改善出来てしまう
- が、プロダクト自体の理解を同時に早く進めないと頭打ちになる(なりました)
- 次からは、まず仕様・プロダクト自体を理解することにもっと注力したい
経緯
ここからは起こったことを時系列順につらつら書いていこうと思います。
チーム結成
ある開発コミュニティで ISUCON の存在を知り、とりあえずなんか Web を早くすればいいんだな〜ぐらいの解像度でしたが、とりあえずぽろっと「出てみようかなあ」と言ったところ、偶然にも他にも出たいと思っていた人がおり、ノリで参加が決まりました。
本番前日
自分の怠惰で本当に何も下調べや過去問解きをすることなく本番前日を迎えました。本当にバカ。
またチームメンバーの @re-taro が当日別の予定が入ってしまい参加できなくなり、2人参加が確定しました。
事前の準備が無いぶん、コミュニケーションコストだけは低くしなきゃと思い、もう1人のチームメンバーしゅんの家にモニター等一式を抱えて転がり込み、泊まり込みで臨みました。
あまりにも適当に運搬されるメインモニタ
到着後流石にやばいと思い、ここからひたすら先人の皆さんの参加記を読み漁りました。そこで Go, nginx や mysql と戦う必要があることと、それに対抗するための alp, slp, pprotein といった必須ツールが存在することを知り、これらのデプロイ練習や git 管理のためのスクリプト群をVagrant内で動かす練習等をやりました。
結局本番当日朝6時ぐらいまでかかりましたが、これをやってなければここまで点数取れなかったと思います。どう考えても一ヶ月ぐらい前にはやっとくべき。
検証フロー
基本的なフローは、ローカルでコード編集 → git commit → 各サーバー上で pull してベンチ、でした。各ベンチの排他制御は口頭で済ませました。適時ブランチ切って検証したり、そのまま master 直 push したりしてました。
分担は特にありませんでした。何か思いついたら「???やるね〜」「はーい」でした。
初動
朝飯食っていざ 10:00 開始。言語は情報が多い、かつ pprotein を簡単に刺せるという理由で Go を選択。早朝に練習した git 管理や pprotein のセットアップ等を済ませ、最初のベンチを回しました。
index 貼り + α
とりあえずインスタントな策として index 貼って db の config に秘伝のタレを流し込みました。
鯖分担
どうやら DB の負荷がデカそうだ、ということで DB を早々にサーバー 2 に分けます。
10,000点。かなり脳汁出ました。
が、よく分からないエラー(予期していない状態推移等)でこの先のベンチが失敗するようになったので、一旦この事実は把握しておきつつ、元に戻した状態で改善を進めました。
ライド評価のキャッシュ
完了したライド評価をグローバルの map に持たせることで、「椅子の完了したライド数と平均評価」の取得が高速になり getChairStats
が高速になります。
ride_status 周りの最適化
for
の中で ride_status
を取得しているところを 1 クエリで済むようにしました。
nginx で静的ファイルのキャッシュ + 不足してた index を追加
この辺りは2人で同時に色々検証してたので厳密にどのベンチがどの変更に結びついているか後になってみると分からず定かではないのですが。。
スコアは一気に 3000 ぐらい上がりました。多分貼り忘れてた access_token の index 追加のおかげかな。
椅子の場所のキャッシュ
椅子の総移動距離と現在位置をグローバルの map に持ちます。総移動距離の計算クエリが非常に重たいのが無くなり早くなります。今考えればついでに遅延書き込みできたかも?
スコアはあまり増えていませんが、遅くなっている訳が無いのでそのままマージ。
通知ポーリングレートを落とす & マッチングを増やす
この辺りですぐ思いつく改善はやり切り、マニュアル等とにらめっこしてました。今思い返せば、いちばん頭が働いている朝イチでマニュアルのポイントを洗い出すとかやっといたほうがよかったな。。
通知ポーリングレートが調整可能 & 初期値 20ms であることに気づき、流石に多すぎるだろと思ったので落としたところ、「長期間マッチングが行われませんでした」エラーが出るようになりました。
そこでインターバルで動いているマッチング 1 回につき 1 ライドしかマッチングされないのを、全てマッチングするようにしました。マッチングアルゴリズム自体は、仕様が把握し切れておらず、適当に最適化したらエラーが出たので一旦 for
で回す愚直実装としました。
そしたらスコア爆伸び。。!脳汁やばかったです。(しれっと DB 鯖分離はまたやっています)
終盤
ここからは、通知 SSE できないかなとか、クエリ最適化できないかなとか、色々試していたのですが、結果時間内に出来なさそうだったり、クリティカルエラーが出たりしてなかなか出来ませんでした。仕様理解が足りなかった。。
マッチング周りのパラメータ調整やログ剥がしで最終的に次のスコアでフィニッシュしました。
この時点では全体 3 位学生 2 位だったので、これはかなり良いのでは、、?と思っていたのですが、皆さん最後に追い込んでくる & (この時点では謎の)失格でちょっとがっかりしました。
失格理由判明
翌日に失格理由が「データ保持違反」であることが判明しました。メモリ上にキャッシュするなどの戦略を取っていたのであり得なくは無いのですが、実際問題 insert
や update
のクエリには一切触っておらず、、、この記事を書いている時点でも原因は分かっていません。
公式さんがどう確認されているか存じ上げないですが、流石に人力では無いと思うので、競技中に確認する手段があれば嬉しかったなと思います。
例えば、ベンチ走行後の画面に「データ保持確認」ボタンがあって、自分で対象サーバーを再起動した後、そのボタンを押すとチェックできるみたいな。。
おわりに
ISUCON 初参加でしたが、結果凄く楽しかったですし、勉強になりました!
開催してくださった運営様、および他の参加者の皆様、大変ありがとうございました。
-
負荷走行によって作成されたデータが再起動後に取得できなかったことを意味 ↩︎
-
本番終了後も競技サーバーが開かれたままにされました。本当に感謝。総競技参加者は不明です。が、個人的にブログに書かれたスコア と 2024-01-12 早朝時点での最新スコアを比較してみたところ、実に 740 チームに差がありました。そのうち 81 チームは感想戦からの参加でした。本当に適当計測なので信憑性はありませんが、もし合っているとすれば、結構な数参加されていてびっくりです。 ↩︎
Discussion