ISUCON10に参加しました

ISUCONにチーム名「締切駆動開発」で初参加しました。 今までISUCON出たいなぁと思ってたもののチーム作るのが難しくて申し込みすらできなかったんですが今回は1人参加OKということでえいやっと申し込みました。

結果

言語はPHPで参加して最終スコアは589で惨敗でした。N+1問題の解決に時間がかかってDBのロードバランシングまで頭が回りませんでしたorz.. リポジトリhttps://github.com/tsuyosh/isucon10q/ です

f:id:tsuyosh:20200913063140p:plain
ベンチマークの履歴

タイムライン

10時開始だったのが12時に延期になったので朝食を食べて洗濯しながら準備を進めていました。

12:20 競技が開始されたもののポータルページがBad Gatewayで死んでしまったので復旧を待ちながら事前に決めていたタスクを進めました。

余談ですが今回の真のお題はポータルページだったのでは、、と思うほど運営の方が大変そうでした。お疲れさまです。。 初参加なので以前のポータルページは知りませんがリーダーボードが時系列グラフ付きで見やすくなっていて、リクエストしたベンチマークもリアルタイムで状況が更新されて終わったらNotificationでプッシュ通知してくれるなど非常に便利でした。

sshの導通確認。当日マニュアルの内容を読んでssh configを変更。無事ssh繋がりました。

Host isucon-bastion
   HostName (踏み台サーバーのIP)
   Port 20340
   User isucon
   TCPKeepAlive yes

Host isucon-server1
   ProxyJump isucon-bastion
   User isucon
   HostName 10.162.93.101
   TCPKeepAlive yes
   LocalForward localhost:10080 localhost:80

Host isucon-server2
   ProxyJump isucon-bastion
   User isucon
   HostName 10.162.93.102
   TCPKeepAlive yes
   LocalForward localhost:20080 localhost:80

Host isucon-server3
   ProxyJump isucon-bastion
   User isucon
   HostName 10.162.93.103
   TCPKeepAlive yes
   LocalForward localhost:30080 localhost:80

各サーバーの設定をPHP実装に切り替え

当日マニュアルの確認をしながらAPIの実装をななめ読みする。

踏み台経由でブラウザからアクセスできるのでISUUMOを実際に使いながらAPIを確認。

ソースコードや設定ファイルをgithubにpush https://github.com/tsuyosh/isucon10q/commit/fc77bd929285c4af2370ec0b7650325d383f5391 https://github.com/tsuyosh/isucon10q/commit/e1f30519715bf205ca61bbf715ba61847ccd99f0 https://github.com/tsuyosh/isucon10q/commit/9652fcc398e23e94956e7f8e232b9a435d2ba269 https://github.com/tsuyosh/isucon10q/commit/edf9b7226973070e73c88044510f82ea0b173b1e

ISUUMOを使いながら問題になりそうな部分を付箋に書いていって簡易タスク管理をしました。(この辺は1人チームの楽なところ)

  • なぞって検索のN+1問題
  • 絞り込み項目が多い(price, width, height, depth, kindなど)
  • features検索がLIKEを使った部分一致検索
  • おすすめ物件の検索条件の改良
  • 椅子を買うときのSELECT ~ FOR UPDATE部分の削除
  • お買い得リスト(low_priced)APIの結果のキャッシュ
  • 負荷分散

ポータルページが復旧したので早速初期ベンチを実行。初期値は330。他のチームは510前後だったのでいきなり差がついていた。。

13:57 計測できるようにMySQLのslow queryを出力 https://github.com/tsuyosh/isucon10q/commit/eab0200d33c4a91c3d8c232999edb122c8c2576e

ただ、SQLの条件文が絞り込み項目の数によって動的に変わるのでmysqldumpslowでボトルネックになりそうな部分を絞り込めませんでした。。

13:59 alpで解析できるようにnginxの設定を変更 https://github.com/tsuyosh/isucon10q/commit/53e0650f98174d4b2db8e6cecd0e9c526e65dd61

これもalpの使い方をあまり知らなかったため集計をいい感じにグループ化できなくてCOUNT=1の結果がズラーと並んでしまい活躍の機会がありませんでした。 終わった後でパターンマッチングでグループ化できるのを知りました。

16:00 とりあえずORDER BYでINDEXが使えるように複合INDEXを追加。Scoreが446に https://github.com/tsuyosh/isucon10q/commit/5ea37a3e36aa6d3d1c681e2885ea54f57d4de593

次になぞって検索のN+1問題に取り掛かる。 MySQLはGEOMETRY型にSpatial INDEXが使えるはずなのでカラムを追加してSQL文を変えれば解決するはずと思ってDB schemaの変更とINSERT, SELECT文の修正を行う。 https://github.com/tsuyosh/isucon10q/commit/6ec6ee4f418e665864907d8bbfa247fbf9e302c9 https://github.com/tsuyosh/isucon10q/commit/49673e754b8541ff98eaeb2c6445fb90b2b0e2a6

ただ、ここでベンチマークを実行してもFailになってしまい調べても原因がわからないので一旦revert。この時点で18:11なので2時間位格闘してたことになる。。 https://github.com/tsuyosh/isucon10q/commit/60feecf5381cd6c478ecb4029a5f20172a870887

18:12 椅子の購入時にstockをチェックするために SELECT ~ FOR UPDATE をやってるのが無駄だったので UPDATE ~ WHETE stock > 0 のような感じでUPDATE文1つで済むように変更。ただ、思ったより効果はなくてスコアは437。 https://github.com/tsuyosh/isucon10q/commit/8acb3527d697703f5e39ca5e7f086b870f8a7935

18:41 price, width, height, depth, kindの絞り込み検索はいいアイディアが浮かばなかったのでやけくそで全部INDEXを貼ったものの案の定スコアは上がらず。 https://github.com/tsuyosh/isucon10q/commit/142b0014a45d44a759c3890009c9b5ca7cac5321

再度N+1問題に挑戦。今度はestateの初期データ部分(2_DummyChairData.sql)は弄らず、ALTER TABLE文を追加で実行するSQL( 3_AddLocationToEstate.sql )を追加。 https://github.com/tsuyosh/isucon10q/commit/d0b693c66a32c9f0fcab4695ca46f1cc27fd4555

最初はベンチマークFailに悩まされたもののログを追っていくと init.sh 以外でも initialize APIでも実行するSQLファイルを追加する必要があるのがわかって速攻で追加。これでベンチが通ってスコアが589に伸びた。(最初に初期データのSQLを弄ったのが悔やまれる...) https://github.com/tsuyosh/isucon10q/commit/4b6f6ba4374160d6900ee8d369f3d802d8148cf1

最後にBOT対策をやろうかと作業していたのだが時間が差し迫ってたので無理をせずこのままfinish

予選が終わっての感想

事前に色々準備をして1人でも回るようにしたかったのですがチーム名の如く準備は進まず結局ほぼすべての作業を手作業でやってました。 mysqlに接続するとかログを確認するとかgithubからサーバーにdeployする作業が地味にめんどくさかった。 あとssh接続が何も操作していないとハングって再接続しないといけなかったのが地味に辛かったです。ssh configで TCPKeepAlive yes にしていたのですが。。

予選が終わった直後からDiscordのrandomチャネルで振り返りのコメントを見ていましたが知見に溢れていてずっと感心していました。DBを分割したらスコアが上がったという話を聞いてやっぱIOがボトルネックになってたんだなぁと思いました。あとfeatures検索の対処方法(bitsetとか全文検索とか)もなるほどなぁと思いました。

あとなぞって検索は皆さん苦労されてたようです。(ネタ元の)SUUMOでもすごく便利な機能なのでN+1問題は絶対解決したかったw 解決できてよかったww

今回は1人で参加しましたがよほど自信がない限りはチームを組んで役割分担したほうが良さそう。来年はチーム参加したいです。(本業がAndroidエンジニアなのでチームビルディングは大変ですが...)

参加者やスタッフの皆さんお疲れ様でした。