nakarioのほぼISUCONブログ

ISUCON出るたびブログ書く

実力を発揮できたと感じるISUCON10予選(百万円ドリブン:21位)

ISUCONを始めてから4連続での本戦出場になる。前回までの予選では、意図せず難所を攻略できたおかげで通過できたという感覚が強かった。しかし今回は「ここが今最大のボトルネックだから、これを改善すれば大きく点数があがるはずだ」という認識を持ち、それを時間内に実行することが出来たために21位という成績を残せたと思う。今までの知識と経験が結実したという感覚は予選通過にもまして喜ばしい。
そんなISUCON10予選での判断と施策を振り返る。

チーム

nakario: アプリの計測と改善をメインに行う
aokabi: インフラ、ミドルウェアを担当
murata: 全体を見れる遊撃手

4年連続でこのチームで参加していることもあり、コミュニケーションや事前の練習会などの段取りはスムーズに行えた。今年からmurataは社会人になったけど、練習の時間をとってくれたのでありがたい。
他のメンバーの作業の詳細はブログを書いてくれるのに期待しておく。
リポジトリはこちら。
github.com

当日の流れ

12:20-12:30

アクセスするサーバのIPアドレスとポートを取得し、ssh_configの設定ファイルを書いてScrapBoxで全員に共有。その後GitHubのプライベートリポジトリにDeployキーを登録するため各サーバでssh-keygenしてまわる。

Host isucon-bastion
   HostName <踏み台用IPアドレス>
   Port 20340
   User isucon
   
 Host app*
   ProxyJump isucon-bastion
   User isucon
   LocalForward localhost:10443 localhost:443
   LocalForward localhost:10080 localhost:80
   LocalForward localhost:9999 localhost:9999
   LocalForward localhost:19999 localhost:19999
   
 Host app1
   HostName <サーバ1のIPアドレス>
   
 Host app2
   HostName <サーバ2のIPアドレス>
   
 Host app3
   HostName <サーバ3のIPアドレス>

(この書き方だと複数台のサーバに接続したときにポートが重複してしまうことに後から気づいた。)
netdataやalp、pt-query-digestなどよく使うソフトウェアのinstallはmurataに並列でやってもらっていた。

12:30-12:40

pprofをGoのコードに埋め込む。

12:40-14:00 スコア403-602

ベンチが回せない状態だったためドキュメントを読み込むのを中心に、勘で修正するしかなかった。
なぞって検索に含まれるN+1クエリを解消する。ただしこのクエリはSPATIAL INDEXを使わないと全物件に対して凸包内外判定をすることになり早くならない事は知っていたので動作を確認しただけでマージは後回しにした。
SPATIAL INDEXというかGISについてはたまたま概念だけは知っていた。しかし実際何をすればいいかは全然知らなかったため、DBに詳しいaokabiに任せるつもりで別のタスクに取り組むことにした。
この間murataはBOT対策をしたりコードを複数ファイルに分割したり、aokabiはDBにインデックスを張ったりしていた。

14:00-16:00 スコア509-819

このあたりで再びベンチマークを走らせられなくなる。
アプリ全体のコードを見てN+1になっているINSERTクエリを一括化するよう変更。この時点では効果が薄かったが他の人の感想を見るに後々効いていたらしい。
aokabiは引き続きインデックスを追加したりMySQLをver8にしたり、murataはpprofを参考にレスポンスのJSONに含まれるインデントをなくしていた(終了後debugモードの存在を知る)。

16:00-19:00 スコア905-1327

ベンチマーク中のhtopの観察からDBのCPUネックが最優先事項とわかり、pt-query-digestからやはりなぞって検索が重いのでSPATIAL INDEXに取り組むことに。aokabiに聞いたところDBは他にも改善点があるようだったので自分がSPATIAL INDEX担当に。色々調べて頑張って実装したが、エラーが出まくり最終的に三人で協力して動くところまで持っていった。終了後にgenerated columnという超便利機能を知った。

19:00-21:00 スコア1276-2281

他の参加者のスコアから、このままでは25位以内に入れないと予想していたので、多少無理をしてでも大幅得点アップを狙う必要があった。SPATIAL INDEXを使用してなおボトルネックはDBのCPUだったので、特に何も考えずに「DBを分割(レプリケーション)したら良いのでは?」とつぶやいたらmurataがchairとestateでDBを分けるアイデアに昇華してくれた。最初はレプリケーションを試したものの練習不足から時間が足りないと判断し、murataのアイデアを急いで実装した。終了20分前にスコアの大幅改善を確認し、再起動チェックと各種ログをオフにし、ベストスコアを出して終了。

感想と本戦への意気込み

過去問練習の経験や実務で得られる知識を活用できるが、過去問とは別のボトルネックが用意されたとても良い問題だった。また、他の参加者から色々便利情報を得られたことから相当改善のバリエーションがあると見られ、誰もが楽しめるよう配慮されていたと思う。作問・運営の皆様にはこれほどの素晴らしいイベントを支えてもらっていることに感謝の念が絶えない。
本戦には今回のように実力を発揮できるようなメンタルと事前準備をこしらえて、今度こそ百万円を頂くつもりで参加させてもらう。