tatsumack blog

エンジニアによる雑記です

ISUCON10予選にチーム「カレーおじさん」で参加して16位で予選通過しました

ISUCON10予選に「カレーおじさん」(@tatsumack, @sugaret, @masakingp)というチームで参加しました。
最終スコアは2608で、16位で予選通過することができました。

isucon.net

@sugaret の振り返りはこちら
sugaret.hatenablog.com

予選問題の解説と講評はこちら
isucon.net

チーム「カレーおじさん」

前職の社内ISUCONをきっかけに組んだチームです。昨年、本家ISUCONに初参戦したものの予選で惨敗していました。

事前準備

前回はチームで集まって練習したりしたものの、今回は特に練習する時間を取らずに当日を迎えてしまいました。
チーム練習はしませんでしたが、個人的には、前回のISUCONに参加するときに作っておいた秘伝のタレの見直しやツールの素振り、チームの役割分担の確認を前日の夜に行っていました。
GitHub - tatsumack/isucon-tools: ISUCON秘伝のタレ

こんな感じのスクリプトを用意していました。

  • install.sh
    • alp, pt-query-digestなどの計測ツールのinstall
  • deploy.sh
    • 各サーバーに入って、nginx/mysql/go serverのrestart、git pullしてbuild、access_log/slow_logの退避を行う

使用言語

Go

計測

計測には以下のツール/コマンドを使っていました。

いつもはpprofも使うのですが、今回アプリケーションはボトルネックにならなかったので使う機会がありませんでした。

当日の流れ

レポジトリはこちら
github.com

9:00集合
12:20~
  • 全員でレギュレーションを読みつつ、@sugaretがアプリの挙動確認、@masakingpがローカルでの環境構築、@tatsumackが各種計測ツールを使えるように整えていた
  • ベンチが使えない状態だった
13:15~
  • ベンチが使えるようになった
    • 初期スコアは371
  • nginx、mysqlカーネルパラメータの秘伝のタレを投入 @tatsumack
    • なぜかmysqlのslow queryが吐かれず、原因調査
  • alpによるアクセスログの解析で /api/estate/search・/api/chair/searchのエンドポイントが支配的だということが分かったので、改修に取りかかる @masakingp
    • 検索条件を組み立ててLIKE検索をしてるところが遅そうだから、正規化するなりして上手いこと回避したいですね、という話をしていた
  • botからのアクセスを弾く実装もまあ必要でしょうということで、echoのMiddlewareで弾く処理を実装する @sugaret
    • 実装したものの、botからのアクセスが少ないし、スコアがあまり上がらないなあという話をしていた
    • のちに実装ミスがあることに気付く
  • この頃のスコアは450前後
15:00~
  • slow queryが吐かれない原因を突き止め改修する @tatsumack
  • pt-query-digestで解析したところ、検索条件を組み立ててLIKE検索するクエリは全然なかったので撤退 @masakingp
    • やっぱり計測は大事
  • dstat, topコマンドを叩いて、CPUが100%に張り付いていてmysqlが支配的だったので、slow queryを潰していくところから着手することに
    • indexを貼るだけで解消できそうなクエリがいくつかあったので、indexを貼って回る @sugaret
    • 物件がpolygonに含まれるか判定するクエリはアプリ側で計算できそうだよねという話になり、実装に取りかかる @masakingp
    • Prepareもそこそこ支配的だったので、interpolateParams=trueを入れて、アプリ側でプレースホルダ置換するように変更 @tatsumack
    • order by popularity desc, id asc は昇順・降順が混在していてindexが効かない。実は id asc いらないんじゃね?っていう話になり、@sugaret が id asc を外した修正を入れてみるとなんとベンチが通る
  • スコアが900まで伸びる
17:00~
  • 椅子が物件のドアを通過できるかを判定するクエリが全然index効いてないので改修に取り掛かる @tatsumack
    • (door_width >= ? AND door_height >= ?) OR (door_width >= ? AND door_height >= ?) OR ... となっていた条件を1つの (door_width >= ? AND door_height >= ?) で取得できるようにする
    • 実装はすぐにできたがベンチでFAILして頭を抱える
  • ベンチがFAILしたときに、エラーメッセージが何も表示されないという事象がしばしば発生する。 @sugaret が運営に問い合わせるとエラー内容を教えてくれた
    • 以降も何回か表示されないことがあったので、問い合わせをした
  • select count(*)系のクエリは、件数をカウントする別テーブルを作れば良さそうだよねという話になり実装に取り掛かる @sugaret
    • この改修は結局取り込まれず
  • @masakingp が実装していた物件がpolygonに含まれるか判定する改修が入ってスコアが1095まで伸びる
    • 予選突破圏内に入ってテンションが上ってスクショを撮ったりしていた
f:id:tatsumack:20200916003349p:plain
記念スクショ
  • そろそろ複数台に分けた方が良さそうだよねという話になり、まずはweb1台・mysql1台の構成に変更する対応に取り掛かる @tatsumack
    • 椅子がドアを通過できるか判定の改修は @masakingp にお願いする
18:00~
  • web1台・mysql1台の構成に変更する @tatsumack
    • スコアが1321まで伸びる
  • ベンチが不安定になってきていて、もしかすると id asc を外したのが原因かもということになり、id ascを外した対応をrevertする @sugaret
  • 椅子がドアを通過できるか判定の改修が終わる @masakingp
    • ベンチでFAILしていたのは件数を絞り込む処理が抜けてしまっていたのが原因だった
  • botを弾く対応が不十分だったことに気付いて、修正を入れる @sugaret
    • スコアが1622まで上がるが、ベンチのステータスがCANCELLEDで終わってしまう
    • その後何回かベンチを回すが、スコアは1200~1350で止まってしまう
  • webはリソース余裕あるけど、mysqlはCPUが100%で張り付いているので、3台目はmysqlが良さそうだよねという話になる
  • chairとestateでDBを分割するのが良さそうだということを思いつき、mysql2台構成に取りかかる @tatsumack
  • 最終的なサーバー構成は以下のような形になった
    • 1. nginx / app
    • 2. mysql (estate)
    • 3. mysql (chair)
20:00~
  • mysql2台構成にしたものの、/initializeでベンチがFAILしてしまう
    • ベンチの不具合を疑って、何度かベンチを回すもFAILしたままで頭を抱える
    • 残り時間も少なくなってきたので、3人でデバッグを始める
    • 1台目のmysqlサーバーに/initializeを飛ばし、 1台目の/initialize内で2台目のmysqlサーバーの/initializeを呼ぶという実装を入れていたが、2台目の/initializeで自身の/initializeを呼んでいて無限ループが起きていることに気付く
    • これは今思い返すと、1台目の/initializeから2台目のmysqlサーバーに向けてmysqlコマンドを流すようにすればよかっただけだった
  • 「この修正が間に合わなかったら確実に戦犯だな…」と思いながら、なんとか終了10分前に改修が完了する @tatsumack
  • スコアが2608まで上がる!歓喜
    • ここからベンチガチャをして上振れを狙うか、ここで止めておくか議論になる
    • ボーターは2000くらいだと予想して、ベンチを回さないという判断をする
    • 結果的にはこれが正解だった。僕はベンチを回す方に傾いていたが、2人が冷静だった。感謝!
f:id:tatsumack:20200915223540p:plain:w400
終了直前の奮闘の様子
21:00~
  • 競技終了
  • 最終スコア的には通過していそうだけど、再起動試験を全くしていなかったのでとても不安
24:00
  • 帰りの電車の中で予選通過したことを知る

感想

  • めちゃめちゃ楽しかった
    • 中盤からボーダー近辺をウロウロしていたので、もしかすると予選突破できるかもしれないという緊張感の中で競技を楽しめた
    • 残り1時間は暗雲が立ち込めてたけど、終了直前にスコアが爆増して本当に気持ちよかった
      • 心臓にはとても悪かったし、とても疲れた
  • 良い問題だった
    • 「何をしたらスコアが上がるか全く分からない」みたいなストレスフルな状況になることがなかった
    • 愚直に計測して愚直に課題解決をしていくとスコアが上がっていったように思う
  • タイムマネジメントは大事
    • 今回はギリギリ間に合ったから良いものの、再現性のある勝ち方ではないと思う
    • 競技終了の時間から逆算して、複数台構成への移行/再起動試験/ベンチガチャなどをいつから始めるかは整理しておきたい


ISUCON運営の皆様、ありがとうございました。
本戦もよろしくお願いします!