2020-09-13

ISUCON10 予選通過しました #isucon

チーム名 hidekiy で、@kotaroy と ISUCON10 予選に参加して、何とか通過しました。
ブログ記事の募集期間が終わらないうちに、当日の行動について記録しておきます。

タイムライン

12:20

予選開始
仕様把握、なぞって検索が本丸っぽい予想。
不要そうな node_modules, target を抜いて、コードを Git 管理する。SSH のエージェント転送で連れてきた鍵を使って、サーバーから直接 GitHub にアクセスしてデプロイとかやる事にする。
サンプルの SSH の設定ファイルに、8081~8083 でそれぞれのサーバーのポート80を開けるようにする設定を入れた LocalForward を追加してチーム内に共有した。

14:00

去年のISUCON予選1位チームのありがたい記事 ISUCON9 予選を全体1位で突破しました

 で学んだ Cloud Trace, Cloud Profiler をまず設定する。New Relic はプロファイルが取れれば不要と思ったんで、入れない事とする。リソース監視は top を更新間隔1秒で目視確認して行う事にした。

いずれ問題になってくると思ったんで、nginx.conf でのボット対策を依頼した。

15:00

検索機能がクソ重い事分かったんで、ソート用にいくつかインデックスを追加 (EXPLAIN 確認してない)
MySQL サーバーのスレーブ追加を依頼

16:00

なぞって検索のN+1クエリについて、1段目のクエリのWHERE条件に、後段の条件を追加する感じで修正して対応した。
SELECT *
FROM estate
WHERE
  latitude <= ? AND latitude >= ? AND longitude <= ? AND longitude >= ?
  AND ST_Contains(ST_PolygonFromText(%s), ST_GeomFromText(CONCAT('POINT(', latitude, ' ', longitude, ')')))
ORDER BY popularity DESC, id ASC
latitude, longitude から POINT を作る方法について、いま冷静に見ると ST_GeomFromText は使わずとも、直接 POINT(latitude, longitude) で大丈夫と思います。

16:30

searchRecommendedEstateWithChair の条件式が直そうとしてバグらせた。
いつバグったか最初は不明だったためコミットごとにベンチ実行し、切り分けの後当該コミットを Revert して復旧した。

17:00

椅子購入時のロックが無駄なので UPDATE ~ WHERE ~ で、Affected Rows を見る感じに修正した。
SELECT * を画面上に使うカラムのみに減らしてみたが、FAIL しちゃうので元に戻す (レギュレーション良く読んでない)、あまり減らせそうにないので、放置する事とする。

18:00

MySQL スレーブが出来たので、物件検索を別のDBに向けるように修正した。
やる事が無くなったので、なぞって検索の空間インデックス対応に着手する。

19:00

空間インデックス対応 (estate に POINT 型のカラム location を追加) が完成して、なぞって検索のクエリを修正 (EXPLAIN 確認した)
SELECT *
FROM estate
WHERE ST_Contains(ST_PolygonFromText(%s), location)
ORDER BY popularity DESC, id ASC
この辺でスコア2500くらい獲得

20:00

20時半くらいから謎に FAIL し始めて、initialize 時のスリープとか試したが特に変化せず、再起動したりして何とか最終スコア 2210 獲得して終了。

感想

とても素晴らしい運営と問題作成をありがとうございました。
本選でもよろしくお願いします。

反省点

popularity DESC, id ASC の複合インデックスは、ソート時に使われていると思っていたけれど、使われておらず、その事に最後まで気づきませんでした。同時にデプロイした rent ASC, id ASC のインデックスが多少効いていたことで満足していました。MySQL 5.7 ドキュメント: ORDER BY Optimization も今更ながら読んでます。そもそも複合インデックスの末尾に PRIMARY KEY のカラム入れるのはおかしかったですね😅
なぞって検索のクエリに LIMIT を入れられるはずが入れられていなかった。影響度合いは不明ですが、せっかくクエリで多角形内包条件込みの最終結果を取り出せるように直したのに、無駄に取得してしまっていました。
Echo Framework のデバッグモードがオンで、ログも出したままでした。デバッグモードは影響をあまり認識していなかったのですが、JSON エンコード時に無駄にインデントを追加してくれたりするので、検索結果の巨大なレスポンスの時、多少は効いてきたかもしれないです。
Cloud Profiler は、プロファイル取得にいちいち手を取られずに、パフォーマンス改善で刻々と変化するボトルネックを分析するのに非常に役に立ちました。