ISUCON10予選に参加したので、今日は参加報告と振り返りです。
昨年が初参加だったので今回が2回目の参加です。
ギリギリまで参加登録してなくて、twitterで残り100チーム切ったとかの報告があって、慌てて参加登録して滑り込みセーフな感じでした。(480番だった)
会社の同期5~6人と今年も出たいねと言っていましたが、僕以外だれも参加登録してなかったため、誰と出るかで揉めるのが嫌だったので、今回はこっそり1人で参加しました。
去年の自分のブログ見ながらやること思い出して作業してたので、今年もやったこと書き留めておきます。
使った言語と結果
昨年は、Goやってみたかったので事前にGoの勉強をしてGoで参加しました。
今年は、慣れた言語がいいなと思って、普段業務で使っているNode.jsで参加しました。
大体過去問みるとkoa.jsが多かった気がしてたので、今年もkoa.jsだろうなと思っていたら、いつも業務でお世話になってるexpress.jsだったので、心理的障壁がかなり少なくてよかったです。
余裕なくて動かしてないですが、今年はDenoの参考実装もあってびっくりしました。
次に結果ですが、471点スタートで、最高スコアは1006点、最終スコアは 998点でした。
ちょっと長いですが、下記がスコアの流れです。
19:00頃まで500点台をウロチョロしてましたが、そこから900点台まで一気に上がりました。
どうでもいいですが、ベンチマーカーを一番最初に回しました笑
やったこと
12:20 ~ 13:00頃
sshでマシンに接続できてからは、node.jsのコードをgithubのprivate repositoryへプッシュしました。
github.com
基本的な作業フローは、手元のPCにcloneしてVSCodeで編集して、ブログにまとめやすくしようと思って、一応プルリク作ってmasterにマージしてました。
masterにマージしたものは、sshでインスタンスに入って手動でpullしてアプリケーションを再起動してました。
13:00 ~ 14:00過ぎ
次にレギューレーション全部読んで、 botからのリクエストは 503 Service Unavailable
を返しても良いと書いてあったので、それを実装することにしました。
ISUCON10 予選マニュアル · GitHub
最初Nginxでやろうと思いましたが、Nginxのconfを書くのになれてなかったので、expressのmieddlewareで書いても対して変わらんだろうと思い、Node.jsで実装しました。
このPRのあとから、色々手直ししましたが大体こんな実装です。
uaをみてbotなら503を返すmiddlewareを追加 by ishikawa-pro · Pull Request #1 · ishikawa-pro/isucon10-qualifying · GitHub
なぜか、459点とスコアがちょっと落ちました。
雑にcurlでしか動作確認してなかったので、コードが間違ってたか、リクエスト数が増えてDBのボトルネックがより目立つようになったかと思いました。
今考えたらちゃんと計測すればよかったですが、後者だと信じて引き戻さずに次の作業へ進みましたw
14:00 ~ 16:00 過ぎ
コード読んだり、NginxやMySQLの設定をみたりしつつ、Nginxのlog formatを変えてalpで解析できるようにしたり、MySQLでスロークエリーログを出すようにしてました。
16:00 ~ 17:00 頃
この変は記憶が曖昧ですが、スロークエリーログを見ながらMySQLにインデックス貼ったりしてました。
確か estate
のrentにindex貼った気がします。
ALTER TABLE isuumo.estate ADD INDEX rent_index(rent);
ここでようやく初期スコアをちょっと超えて、518点とかになりました。
17:00 ~ 17:30
alpとMySQLのログから /api/estate/low_priced
がわりとよく叩かれてるのと、 estateのlow_pricedが変わるタミングは /initialize
時と、csv入稿の時だけっぽかったので、オンメモリ上で管理するように修正しました。
533点と思ったほどには上がりませんでした笑
17:30 ~ 18:00 過ぎ
またalp見直したり、slow query logみたりしてて、nazotteがやっぱり遅いよな〜と思ってコード見てました。
とりあえず下記SQLを、取得したesteteの数だけfor文で実行していたため、非同期処理をシリアルに実行するようになっていたのが気になったので、Promise.allでパラレルに取得できるように修正しました。
"SELECT * FROM estate WHERE id = ? AND ST_Contains(ST_PolygonFromText(%s), ST_GeomFromText(%s))";
これまたなんと、517点とスコアが落ちました。
コードは間違ってなかったのできっと、複数のSQL実行をシリアルからパラレルに修正したことでDBの負荷が上がったんだと信じて、これも引き戻さずに次へ進みましたw
18:00 ~ 19:00
結構お腹が空いてきてました笑
確かこの時間くらいに、nazotte用にestateにインデックスを貼りました。
ALTER TABLE isuumo.estate ADD INDEX lat_long_index(latitude, longitude);
531点とか落ちたスコア分が元に戻るくらいには上がった気がします。
そのあとは、コードを読んでも、極端に複雑なSQLが沢山あるわけでもないし、椅子の購入や資料請求もシンプルだったのでまだボトルネックではなさそうだなと思ってました。
19:00 ~ 20:00
マシンスペックが1コア2GBで、ベンチを回しなが htop
を見てるとメモリはちょっと余裕あるけど、CPUは100%で張り付いてるな〜という感じでした。
まだMySQL自体のチューニングがほぼ手付かずの状態だったので、そっちの方をやるかという感じになって色々ググってました。
最終的にthred_cache_size, query_cache_size, innodb_buffer_pool_sizeなどをいじりました。
query_cache_sizeとquery_cache_limitはどれくらいが適切がよく分からなくて感覚で設定しました。
この辺は来年までに詳しくなりたい。
thread_cache_size = 151 query_cache_limit = 50M query_cache_size = 512M query_cache_type = 1 innodb_buffer_pool_size=1G innodb_log_file_size=250M
この設定がすごく効いて、一気に900点台までアップして、色々微調整して1006点まで行きました。
このスコア見たときは声出ましたw
19:00過ぎで、たしか順位が真ん中くらいだったのが一気に61位まで上がったので結構テンションあがりました笑
結局パラメータいじり過ぎて、一番よかったパラメータが分からなくなり977点で着地。
ちゃんといいスコア出たときは、記録せねばと猛反省しました。
20:00 ~ 21:00
残り時間で何ができそうか考えて、インスタンスを1台しか使ってなかったので、1台目をMySQL専用機にして、とりあえず2台目をアプリケーション用サーバーにしてみようと思いました。
しかし、MySQLに繋ぐためにポートを開けないといけないが、この時間に焦って慣れないことしてインスタンスをダメにしたらヤバイなと思って諦めました。
来年までにLinux力も絶対上げたい。
最後の最後に /api/recommended_estate/:id
でindex貼れそうだと気付いて、
ALTER TABLE isuumo.estate ADD INDEX door_index(door_height, door_width);
を貼ったら998点になり、slow query logの設定とかを無効にして終了。
後日公開された参考値も同じ998だったので再起動試験も問題なしでした。
感想
昨年は3人で出て、簡単な N+1を直して微妙にスコア上がって終了だったのが、今年は1人参加で初期スコアから倍以上アップして、後半に一時61位まで上がったので、1年で成長をすごく実感できてよかったです。
結局ラストスパートでめちゃくちゃ抜かれていったみたいで悔しかったので、来年こそは決勝いけるように頑張ります。
最後に、運営の皆様、お疲れ様でした。
決勝出場するチームの皆さんは頑張ってください!