ISUCON8予選は学生枠6位で本戦出場が決まりました

ISUCON8の予選に、メルカリで一緒だった@zaq1tomo@inatonixzin-gonicというチーム名で出場しました。
結果は学生枠6位だったので、やったこととかまとめます。

事前準備

とまあ初出場ということもあり結構ちゃんと準備して、何も言い訳できない状況だったのでとりあえず通過できてよかったです

やったこと

チューニング対象は、イベントの座席予約アプリでした。
言語は、3人の技術スタック的にGo一択でした。

nakabonne/isucon8-qualify - GitHub

@nakabonne: インフラ担当
@zaq1tomo: アプリ担当
@inatonix: アプリ担当

序盤

@nakabonne

リバースプロキシは完全にnginxのつもりでいたので、h2oアクセスログをalpがパースできる形式で出力するのに結構時間かかってしまって、大迷惑かけてしまいました。
本戦では爆速でやりたいです。
ちなみに今回はnginxに差し替えてる方が結構いたので、インフラ担当としてその辺のトレードオフも見積もれるようになりたいです。

@inatonix, @zaq1tomo

方針決め

リソース

弱者の戦い方として、無理に複数台構成はしないと最初から決めていたため、ゴリゴリインメモリキャッシュを使おうと話していました。
実行時にCPU使用率が100%に張り付くので、なるべくredis等の別プロセスは立ち上げたくないと感じたので、キャッシュするならGoプロセス管轄内のメモリに乗せようとなりました。
ただ、mariadbが大分CPU食っているのでdbだけ別インスタンスに移したい気持ちはありました。

スロークエリ

reservationsテーブルから全座席ごとにselectしてるクエリが37万回呼ばれていて、これを解消しない限り次に進まないことがすぐに分かりました。
今回はボトルネックが明白だったため、学生でも迷わずに進める良問でした。

アクセスログ

一番上の /api/users/:id で、先程の重いクエリが流れていたため、平均9秒かかっていました。その他レスポンスタイム上位のエンドポイントはほとんど同じクエリがボトルになっていました。

中盤

@nakabonne

reservationsテーブルにはevent_idcanceled_at IS NULLにマルチカラムインデックス張るのが正解だったっぽいです。
単体テーブルへのクエリならまだいいのですが、内部結合となると実行計画が全く理解できず死亡しました。

@inatonix, @zaq1tomo

僕のアクセスログ解析が遅れたせいもあり、全くスコアが上がらず完全にお通夜モードになっていた15時頃(だったかな)に、@zaq1timoのN+1修正が火を吹き1000点→10000点と10倍になりました。
/api/users/:idの平均レスポンスタイムも9秒→1秒まで解消していました。

終盤

@zaq1tomoが合計料金をSUMするクエリをチューニングしてくれてちょっとスコアが上がり、諸々のログを吐かないようにしたら13000点を超えるようになりました。
ただ、ランダムにfailしたりしてハラハラしていました。
競技30分前に再起動し最終的に14000点を超えたところでコードフリーズし、お片付けを始めました。

感想

1392名参加している大会で、障害等を何も起こさなかった運営の方々がかっこよすぎました。
今回は各チームごとにベンチマークを用意してくれた(構成的にするしかなかった)Conohaさんにも感謝の念が溢れます。
本戦勝つぞー