ISUCON11に参加して予選敗退した

2021/08/21に開催されたISUCON11予選に参加して200位くらいで敗退しました。言語はNode.jsで25000点位になりました。最終的にapp1台、db1台の構成にしました。

portal.isucon.net

当日の流れ

10:00頃

まずは環境を作成しないといけないためAWSGUIから作成。その間にルールを読むなどしました。TypeScriptで書かれていたり、MariaDBだったりということも判明しました。
ひとまずベンチ用のスクリプトを導入してベンチを回し、kataribeやpt-query-digestで解析しました。その他、ベンチ前後に動かすスクリプトを作るなどもしました。

DBを見るとインデックスが全く張られていなかったので張った(つもりになった)り最新の情報を取るためにN+1問題が発生していたので最新の情報を持つテーブルを作ってそちらに情報を入れる用に変更した(つもりになった)りしました。しかし点数は1000点からあまり変わりませんでした。

11:45頃

ベンチ前にサーバ再起動が必要なのに気付いてはいたものの毎回ビルドが必要なことに気付かず、ずっと最初のコードで動かしていたことが分かりました。ビルドしたことで3500点位になりました。

13:00頃

最新情報を取るところがまだボトルネックだったため、DBではなくインメモリで持つようにしたところ8500点位になりました。これによってマルチプロセスやマルチVMへの移行が難しくなりました。

13:45頃

SQL文でCOUNTを使っている所で全体にロックがかかっているかもしれないので行ロックで済むように変更したり、使っていない最新情報のテーブルへの挿入をやめたりしたら9000点を超えました。
最新情報を取ってきてる所を一部インメモリから取るようにしたら10000点を超えました。

9割落としているリクエストの割合を少し上げたら過負荷からかスコアは落ちました。

16:30頃

インデックスを張ったクエリがスロークエリトップになっていたのでexplainで確認したところインデックスを使わずにクエリが実行されていることが分かりました。

initializeにDBを作り直しているので手動投入したインデックスが意味をなしていないことが分かり、初期化時に追加するように変更しました。

他にも無駄にトランザクション処理になってるところを止めたり、/api/trendでもインメモリの情報を取るようにしました。すると6000点となりなぜか下がりました。

17:00頃

/api/trendで一定時間内にアクセスが有ったら304を返すようにしました。

また/api/condition/でデフォルトで9割のリクエストを落としているのですが、スコア計算を見るとWorstとBadで必要な情報が2倍程度なのに点数が6倍と効率が良いので一部のユーザのリクエストだけ全て通して他は全て落とすというようにしました。
今回はUUIDの1文字目が0/1の時だけ通すようにしました。(wikipediaによると一文字目は8-fらしいのでリクエストが来た理由は分かりません)

点数はなぜか更に下がり5000点位になりました。

17:30頃

終了1時間前からは再起動テスト予定だったのですが、ダッシュボードにアクセスできなくなり競技延長予定とのことだったので実装を続けました。

/api/isuについてもユーザ毎に304を返したり、304を返す時間を延長したり、/api/condition/で受けるリクエストの割合を2倍にしたり、loggerを停止したりしたところ15000点になりました。

その後

最終的に競技時間は18:45位になりました。
/api/condition/で3/8くらいのリクエストを受けるようにしたり、VSCodeRemoteを停止したり、不要なサービスを止めたりして何度か再起動試験をしたところ25000点位に到達しました。

感想

事前準備は何もできませんでしたが当日は測定してボトルネックを改善するというサイクルがうまく回せていたと思います。一方でTypeScriptの型定義を新しくしなかったのでコード中でanyが大量発生し書き辛いJavaScriptみたいになってしまっていました。
最後までINSERTがスロークエリとして残ってしまったので、何か改善策があったのか気になります。