くりにっき

フルスタックキュアエンジニアです

ISUCON 13に1人チームで出場して再起動試験でfailした話

tl;dr;

タイトルが全て

今年の準備

ISUCONの前の週に Google Cloud Next Tokyo ’23 に登壇した関係で今年の準備期間は実質1週間程度でした。

とりあえず勘を取り戻すために下記の素振りをしてました。

github.com

github.com

リポジトリ

競技用のリポジトリ

github.com

ISUCON用の汎用Itamaeレシピ集

github.com

PRとDatadogを見ながら当日やったことを振り返り

当日10:00〜18:00のDatadog晒し

sinatraAPM

競技中はここを見るのが一番多かったです。

サーバ全台

puma

pumaのメモリ以外のメトリクスが後半しかないのは、DatadogのPuma Integrationの設定を入れたのが後半なため。

MySQL

具体的にやったこと

github.com

CloudFormationから起動した直後のインスタンスにベンチマーカーを実行(スコア:4148)

いつものスニペットをコピーして参照実装をGoからRubyに変更(スコア: 3781)

github.com

この時点でRuby 3.3.0-devにしてます。

Sentryとddtrace(DatadogのAPM)を有効化(スコア:3497)

github.com

ここでようやくスタート地点

rubocop-isucon導入(スコア:3389)

github.com

いつもならこの時点でN+1がどれくらいあるかindexを貼る箇所を見つけているのですが、今までとコードの書き方が微妙に違うせいでASTの構成が変わって Isucon/Mysql2部署のcop が全く動かなかったのがショックだった、、、

GET /api/user/:username/icon : DBにある画像を逃がす(スコア:3827)

Datadogを見たら GET /api/user/:username/icon が明らかにボトルネックになってたのでこいつの改善に着手。

エンドポイントを選んでDurationの降順してボトルネックを発見。

小ネタですが https://github.com/sue445/isucon13-20231125/blob/main/ruby/config/ddtrace_init.rb#L55-L65 のようなモンキーパッチをあててSQLの生クエリ(プレースホルダ? が入ってないやつ)をDatadogに送信してexplain取りやすくしてます。(ユーザが入力したパスワードもDatadogに全部生で送信されるので本番アプリには絶対にいれたらダメなパッチ)

GET /api/user/:username/icon : 画像をnginxで返す by sue445 · Pull Request #7 · sue445/isucon13-20231125 · GitHub

ソース見たらDBに画像が保存されてたのでnginxで返そうとしたのですがここで1時間以上手こずったと思います。

ChatGPTに聞きながらやってもいい感じのnginxの設定が書けずに最終的にローカルに保存した画像をsinatraで返すようにしました。

最終的にこんな感じでボトルネック改善したのを確認。

これでもまあ悪くないので1時間もかけずに早いところ諦めるべきだったね、、、(1人チームだとハマった時に無限に時間が溶ける)

fill_user_responseでDBの画像を使わないようにする(スコア:4518)

github.com

最後の方で気づいたけど画像を保存する前に計算したhashをDBに入れた方がよかった。

2台目のサーバをDB専用として投入(スコア:6893)

github.com

ここも微妙にハマったやつ。

デプロイ時に1台目のサーバのMySQLを止めて2台目のサーバのみMySQLを起動してるのに、デプロイ後になぜか1台目のサーバも止めたはずのMySQLが復活してむっちゃわけ分からずにハマってました。

最終的にはserviceファイルのAfterとRequiresが原因とわかって消したんですが、そうしたらPowerDNSが使ってるMySQLにも影響して復活させました

MySQLのスローログを無効化(スコア:7028)

github.com

どうせ見ないので無効化した

index追加祭(スコア:7132)

github.com

github.com

github.com

github.com

POST /api/initialize でやってるTRUNCATE TABLEだとindexが消えなくて、かといってMySQLだと ADD INDEX IF NOT EXISTS のような冪等性のあるDDLが分からなかったのでinitializeで実行されるsqlファイルに入れるのを諦めて手動で投入することにしました。(gitの履歴としては残したいのでこういうファイルだけcommit)

livecomment_modelがnilってNoMethodErrorになったので修正

index追加中にこういうエラーに遭遇したので修正。こういう時sentry使ってると検出できて便利っすね。( https://sentry.io のqueueが詰まってたせいか分からないけど最初の方は全然エラーが通知されなくてつらかったけど)

github.com

github.com

3台目投入(スコア:7228)

github.com

app専用として3台目を投入して負荷分散しようと思ったんですが、appがローカルのPowerDNSに依存しててそのPowerDNSを別サーバに動かすのが手こずりそうだったので実際の負荷分散は後回しにすることに。

17:21頃に再起動試験

appを1台で動かす前提にする(スコア:6949)

この時点で残り時間1時間きってたので危険な変更はしないようにしました

github.com

最後にDatadogとか諸々無効化(スコア:10663)

github.com

最終スコア

isucon.net

10,663 sue445.members.count==1 (再起動後スコア0)

ええ。マジ、、、

競技時間中に再起動試験して問題なかったのに、追試の再起動試験でスコア0になるとは思わなかった。

来年に向けて