valid,invalid

関心を持てる事柄について

ISUCON10 予選敗退した

ISUCON10にソロチームBPM200で参加し、最終スコア770で予選敗退しました。通過スコアには大きく届きませんでした。

戦略

ソロなので時間は絶対に足りない、という前提のもと予めフォーカスするポイントはある程度決めておきました。

大方針としてはなるべく1台で勝負する、ということ。複数台構成に持っていくのは自分のスキル的に時間がかかりそうだし、過去問で素振りしてたとき、問題によってはアプリケーションレイヤでの改善に絞ってもそれなりに伸ばせると思ったため。今回もそうだと良いな〜と思ってましたが…現実は非常。

また、普段使わない解析ツールをインストールしたりログ設定を行ったりするのにかける時間もないため、それなりに手慣れたNewRelicでボトルネックの推移を見ながらチューニング対象を決めていく、というのも予定していました。

やったこと

環境準備

ssh設定確認したりgit initしたりマニュアルを読んだりマシンスペックを確認したり。

bot対策の概念を知ったけどボトルネックなのかどうかわからないので初手では手を出さないことに。

NewRelic導入

素振っていたとおり初手でNewRelicを入れました。導入後、最初に回せたベンチマーカーで出たスコアが280ぐらい。

ベンチマーカー流しつつtopコマンド眺めているとDB, CPU律速で、アプリケーションとしては物件検索と椅子検索がmost time consumingとNewRelicから理解しました。

f:id:ohbarye:20200920145442p:plain
NewRelic便利

インデックス追加

初期実装ではインデックス皆無なので物件検索と椅子検索のコードを読みながらインデックスを追加しました。複雑なパターンすべてに対応はできないだろうと思いながらも、Nginxのアクセスログを見ると単一条件のクエリがそこそこあり、スコアがちょっとだけ伸びました。

このときMySQL5.7の降順インデックス周りの挙動を知らず、EXPLAINしてfilesortになっているのはなんでだろう…とか思ってました。

範囲検索からID検索へ(失敗)

物件検索原因は範囲検索に対して有効ではないアプローチと考え、rentやwidthのidで検索できるように実装を変更。

generated columnにするとかadd columnして一括updateするとかすれば良かったのですが、何を血迷ったか、rent, width, heightのマスタテーブルと中間テーブルを作り、initialize処理の中でレコードをインサートしまくるという方針で実装してしまいました。

その実装に合わせて検索や物件追加のエンドポイントのコードも直す大掛かりな作業になったのですが案の定initializeでタイムアウトする結果になり…泣く泣くrevert。

ぜんぜんISUCON向きの実装ではないし今思えばいろいろツッコミどころがあるのですがここに3時間ぐらい使ってしまったのがこれ以降の思考に大きく影響を与えることに。

Bulk insert

検索周りで数時間溶けた失敗が心に来たので少しでも稼げそうなところを見つけては直してくことにしました。

物件と椅子のN+1インサートはまとめてやることに。このエンドポイントはボトルネックでもなんでもないということを知りつつ…!

物件検索結果をメモリキャッシュ

Nginxのアクセスログからだいたい同じクエリが飛んでくることはわかっていたので、検索条件で結果をキャッシュすることにしました。

これはちょっと効きました。

なぞって検索のN+1なくす(失敗)

最後の2時間ぐらいでもまだ物件検索と椅子検索が支配的ではあったもののなぞって検索が上位に追い上げてきたので見てみることに。わかりやすいN+1があったものの位置情報まわりで何してるかを理解するためにMySQLのドキュメントを読むのにそこそこ時間を使ってしまいました。

f:id:ohbarye:20200920154953p:plain
nazotte

POINT型カラム追加を試したり、クエリをマージしてN+1を直したはずがベンチマーカーが失敗するようになり、デバグを途中で断念…。

ここも成果ゼロにも関わらず2時間ぐらい溶けてました。

low_priced API用のindex追加

安い椅子と物件を検索するAPI (low_priced) もそこそこ時間を食っていてindexが効いてないことを見つけたので追加。

bot対策(失敗)

残り時間がなく、すぐに点数が伸ばせそうなものとしてbot対策があったことを思い出す。Nginxの設定をググりながら入れてみる。

が、スコアがさほど伸びていないため最後の時点でもbotからのアクセスは来てなかったとはず…。明らかに優先度判断ミスっていました。

その他

最後の1時間で細々とした仕上げをしていました。

  • RACK_ENV=productionを設定
  • NewRelicをオフにする
  • Nginxなどの各種ログをオフにする
  • 再起動して問題ないか確認

反省

明らかに点数を稼げる大型の問題には気付いたものの、物件検索の改善アプローチを間違えて時間を溶かしてしまってからは明らかに焦ってしまい、冷静な優先度判断や時間配分ができずに終わってしまった…という感じです。小さい点数稼ぎはできてもブレイクスルーがなかった。

そうなってしまった原因はMySQL周りの力不足。generated column、降順インデックスだけでなく位置検索・空間インデックスあたり(この辺は他の参加者も苦戦していそうでしたが)の知識がなく誤ったアプローチをしてしまったり、DB分割はアイデアすら出てこなかったな〜。

感想

結果は惨敗でしたが自分の力不足を強く感じることができたうえめっちゃ学びがあったので参加できてよかったです。

噛みごたえのある良問を作ってくださったyosuke_furukawaさん、過去にない規模のチーム数にも関わらず捌き切ってくださった運営の皆様、スポンサーその他関わった大勢の方に大感謝です。

来年も楽しみにしています。