ISUCON9 本選で4位になれました

去年に引き続き今年もISUCONの本選に参加することができました。 今年の予選は全体の31番目でギリギリ本選は出れないところでしたが

motemen.hatenablog.com

この方々の誠実さのおかげで本選に出れることになりました。

繰り上がりで出ることになり、この方々の分も、とか、ワンチャン敗者復活からの優勝を果たしてサンドウィッチマンみたいに…!とか思ってました。

最終スコアは 13,016 で、まさかの4位!あと1000点で賞金圏内の3位だったって思うと少し悔しいですが、大健闘できました。ちなみに去年の本選は24位くらいで、得点が出たチームの中での下から2番目くらいでした。そのリベンジが果たせたことが個人的に本当に嬉しいです

isucon.net

チーム構成

言語はPHPで、メンバー構成は

  • アプリ 2人
  • インフラ 1人

サーバー構成は最終的に a: web c: db

bは使えなかった、という感じだった気がします。

何をしたかの記録

当日の記憶が怪しいですが。リポジトリはこちら

GitHub - shmurakami/isucon9_final

去年の本選の反省から、まずコードを読んで仕様を理解する ということから始めます。去年の本選はSNS告知(だったかな?)のフラグに最後まで気付かないという失態を犯していたので、とりあえずコード読まないと話にならんという学びがありました。

コードを読みながら、どうせ使うだろうということでRedisの導入をしたり、NewRelicの導入をしてもらったりしてました。

今回の問題はデフォルトで貼られているインデックスがほとんど無く、初期状態ではベンチマークが回らない前提だったのでベンチを回す前にクエリを見て最低限のインデックスを貼っていきます。

最低限のインデックスを貼ってベンチを回してもずっとFailしている状態が続き、途中で数値しか入らない train_name のカラムがvarcharなことに腹を立ててint に変えたりしましたが、ベンチが通らないので日和って戻したりしてた気がします。

ベンチマークのエラーメッセージが何を言ってるか分からずベンチのバグなのかこちらのバグなのかの切り分けのために運営に聞いてみたりしました。

f:id:shmurakami:20191010133935p:plain

仕様だということで理解。 何が不正なのかが分からずデバッグのしようも無いのでなかなか苦しかった。

後で分かりましたが、ベンチが通らなかった理由は

元々KEYが無かったテーブルにインデックスを追加したことで、取得する順番が変わってしまった

のようです。 「何もしてないのに壊れました」と言ってますがお前インデックス貼ってるやんけ という感じでちょっと何言ってるか分かんないですね。

このときはクエリにorder byをつけて解決したようです。どうやって気付いたか覚えてないけど、多分画面を見ておかしいと思ってorder byをつけたらベンチが通ったのでしょう。train_nameがvarcharなのでインデックスでおかしくなったので、intに変換してあげれば多分通ったんじゃないかな、という気がしてますが試してみたいですね。 結局ベンチマークが初めて通ったのは12:58:36でした。長かった… このときのスコアは 1,138

ベンチが通ってようやくパフォーマンス改善に取り組めるようになったので、N+1の改善とクエリの実行計画の改善、master系のテーブルの中身をメモリ上に乗せてRedisもMySQLも叩かなくていいようにしたりしてました。

全体をちょっとずつ触ったところで available_days を50にあげてみたところ、スコアは4,374まで上昇。

cancelのエンドポイントが遅かったのが気になりましたが、

  • 外部APIに依存しておりそっちはどうしようもない
  • DBのロックも範囲が狭いし多量のHTTPリクエストが直列発行されてるわけでもない
  • PHPなので非同期リクエストは無理
  • 処理できたところでキャンセルは得点が低いから別の方を優先したい

という理由で捨てました。

この辺りでボトルネックが大体 search, reserve 辺りまで絞れてきたのでこの辺の改善をしていくことに。

やることは特に変えずに、仕様を理解、クエリを改善(インデックス、不要なテーブルの参照を除去)、ということをしていって、

16:41:10 5,952

16:59:33 9,529

と順調にスコアを上げられていきました。

そろそろavailable_daysをもうちょっと上げて別の問題が出るかどうか見た方がいいんじゃないか、まだやれることはあるし後でいいんじゃないか、という葛藤を脳内でずっとしてましたけど、堅実にやれることから潰していったのは良い判断だったかな、と思います。

その後も運賃計算周りでDBにアクセスする必要をなくしたりと改善を続けて、 17:47:13 11,376 まで上げることができました。

この辺で、01_schema.sql に追加し忘れてるインデックスがあることに気付いたのはラッキーでした。このまま行ってたら再起動試験後にひどいことになってしまうところだった。

終了時間ギリギリまで available_days の変更とベンチマーク実行を繰り返してましたが、結局available_daysは70に落ち着きました。オリンピック時期は負荷が上がるということでしたが、7月末なので、240くらいまで上げないと効果が出ないのかな…?

手元での最終スコアは 11,415 でしたが、最終の再起動試験では 13,016 まで上がっててちょっと驚きました。メモリとかの問題かな。

締め

去年からISUCONに出始めて、2年連続で本選にまで出れてめっちゃ楽しかったです。 来年は今のところ出ないと思いますが、機会があれば是非また出たいですね。ありがとうございました🙏