はじめに
「BruteForce」(@HF, @hosshii, @oribe)でISUCON10予選に参加し、学生枠で予選を通過しました。
Go言語を使用し、参考スコアは1642です。
@HFは初参加、@hosshiiと@oribeは2回目の参加でした。
事前準備
ISUCON9の問題を使用した部内ISUCONで練習を行いました。
一度各自で解いた後、チームの連携練習としてもう一度はじめから解きました。
その際に役割分担の決定や便利Makefileの作成などを行いました。
役割分担は
- 本番環境操作・計測
- @oribe
- 改善
- @HF
- @hosshii
という大まかなくくりです。
便利Makefileはto-hutohuさんのMakefileをベースにしており、slow queryやkataribe、pprofによる計測結果をslack catでslackに送れるようになっています。
追加した要素は
- 計測結果を送信する際にgitのどのコミットでベンチマークを行ったのかも送るようにした
- git管理に載せているMySQLやNginxの設定ファイルを適用するコマンドを追加した
などがあります。
当日
通話をDiscordで繋ぎ、チャットが必要な場合はSlackを、情報の整理・共有にはHackMDを使用しました。
12:20 競技開始
しばらくベンチマークを回せない状態が続いていたため、各自できる準備をじっくり行いました。
@oribeがサーバーに入ってコードをgitに載せている間に、@HFと@hosshiiがレギュレーションを読み込みました。
コードを共有してからは@HFと@hosshiiがテーブルへの読み書きを行っている箇所の書き出しやボトルネックになりそうな箇所の探索、@oribeは計測を行うための設定ファイルなどへの加筆を行いました。
ベンチマークが回せない中でのボトルネック候補の探索ではgo_oneの結果も参考にしました。
13:20
ベンチマークが回せるようになったので初回のベンチマークを行いました。
この時点のスコアは480です。
13:30
初期状態ではインデックスがidのみだったので、ソートに使われているestateテーブルのrent, popularity及びchairテーブルのpopularity, priceにとりあえずインデックスを貼りました。
この時点のスコアは592です。
15:40
go_oneでnazotteのところがN+1じゃないかという検討がついていたのでそこをしばらくみていました。for
文中で使われているST_Containsのところはポリゴンの内部に点が存在しているかどうかを判断しているだけみたいだったのでアプリ側で処理するようにしたところ、点数が上がりました。golangでのCrossing Number Algorithmを参考にしました。
加えてMySQLのmax_connectionsやカーネルパラメータ、nginxなどの設定を行いました。
この時点のスコアは1001です。
16:30
ベンチマーク時にMySQLのCPU使用率がかなり高かったので、サーバーの2台目をDBサーバーにしました。
これによりスコアが1200点くらいになりました。
この後しばらくportalの不具合によりベンチマークを回せない状況が続きました。
3台目のサーバーもDBサーバーとして使うことを考え、2台目のサーバーと共にMariaDBをインストールしたりしました。
18:10
mariadbの方がいいという噂を聞いたので2台目のサーバーをMariaDBに変更しました。しかしその状態でベンチマークを回したところ、スコアが600点台に落ちてしまいました。
19:30
原因究明を行うよりもMySQLに戻す方が確実だと考えたのですが、その作業の際に@oribeが2台目のDB環境を盛大に破壊してしまいました。
それにより2台目をDBとして使うことが不可能になったため、1台目をDBサーバーに、2台目をアプリケーションサーバーに変更しました。
幸い各サーバーの状態を別のリポジトリで管理していたため、それぞれにもう片方のリポジトリをクローンするだけで役割の入れ替えは完了しました。
これ以降は「2台目に反映したい変更を1台目のリポジトリにpushしました」「1台目のリポジトリって元1台目のサーバーのリポジトリのことですよね?」という大変ややこしい会話が飛び交うようになりました。
20:00
DBサーバーのCPU使用率が100%に張り付いていたので、3台目もDBサーバーとして使うようにしました。
3台目は既にMariaDBに変更してしまっていますが、MySQLに戻そうとして再び破壊してしまう可能性に怯えてそのまま使用することにしました。
今回はテーブルが2つしかなかったので、chairテーブルにまつわるクエリを全て3台目に送るようにしました。
このあたりからいくらベンチマークを回してもFailでスコアが0になり、かつconclusionに何も出力されないという状況が常態化しました。
問題なく正の点数がとれていた状況に戻しても同じ結果になったこと、ベンチマーク実行時の経過スコアは正しく吐かれていたことから、ベンチマーカーのバグであると判断しました。
競技終了後にベンチマーカーの状態を修正した上で全チームに対して追試を行うというアナウンスがされていたので、それにかけることにしました。
競技終了1時間前にはコードフリーズする予定だったのですが、ベンチマーカーのバグと判断するまでにかなりの時間を費やしてしまい、すでに用意できている改善を組み込んでのベンチマークを回せていなかったため、コードフリーズを20:40にずらしました。
20:30
SQLのスロークエリログを見ながらexplainをしたところ、うまくインデックスを使ってくれていないことが発覚しました。複合インデックスを使ったりDESC指定したり(後に知ったのですがMySQLのバージョンが古くて対応していなかった模様)しましたが、どうにも無理そうだということでpopularityのインデックスをはがし、estateテーブルにwhere句で使われていたlongitudeのインデックスを追加しました。
最終的に追加したインデックスは、estateテーブルがrent, longitude、chairテーブルがpriceのみでした。
ベンチマークでは相変わらずFailになりましたが、途中経過で表示されるスコアは1400点台に載るようになりました。
ISUCON公式Discordのrandomチャンネルで「何故かベンチマークがコケる状況が続いたが運営に問い合わせたらすぐに修正された」という情報が共有されたので、運営への問い合わせを行いました。
残り時間からして時間内に対応される可能性は低いことはわかっていましたが、「私たちのスコアが0なのはベンチマーカーの不具合が原因の可能性があります」と主張した爪痕を残すことが大事だと考えました。
アナウンスはされていたものの、本当に全チームに対して追試をしてくれるとは信じ切れていなかったので……
20:40
コードフリーズして再起動試験を行いました。
ちゃんとベンチマークの途中経過のスコアは上昇していく様子が確認できたので、問題ないと判断しました。
その後ログを切ったりしてお祈りタイムに入りました。
一度くらいはFailせずに通らないかな、とベンチマークガチャをしてみましたが尽くFailしました。
追試では正の点数が出ますように、というお祈り感がより強くなりました。
現状の正確なスコアはわからないものの、正の点数が出ればギリ学生枠に食い込めるかどうかくらいだろう、という印象でした。
20:58
すでにmasterに取り込み済みだった、nginxでbotを弾く設定が適用されていないことが発覚しました。
$http_user_agent
を用いてレギュレーションに書いてあった正規表現のものを弾くようにしましたが、半角スペースを含むものがnginxの文法チェックで怒られるなど動作に不安が残っていました(担当した@HFは正規表現&nginx初心者)。
残り2分で適用することは可能でしたが、その設定を反映した上でちゃんと動作するかの確認をする時間はなかったことから設定の反映を断念しました。
結果発表
チームメンバーが誰もISUCONの予選ライブを観ていなかったので、学生枠で予選突破していたことを又聞きで知りました。
感想
最後0点しか出なくなった時は不安でしたが予選を通ることができて嬉しく思います。他のチームを見るともっと改善できる点がたくさんあると感じたので本戦までにはもっと仕上げたいと思います。