ISUCON11の予選を突破しました!

今年もここ数年と同じメンバー(@mot, @syusui)で「チーム五年目」という名前で参加して、ギリギリ24位で予選を突破できました!! 本戦出場はISUCON 7以来なのでとてもうれしいです。

やったこと

言語はGoでやりました。(毎年ISUCONのときだけGoを書いている気がする。) 以下時系列順にざっくり起きたことを書いていきます。

  • mot: インフラ構築、kataribeとpt-query-digestを使えるようにする
  • mot: isu_conditionに(jia_isu_uuid, timestamp)の複合インデックスを作成
    • pt-query-digestで明らかにslowになってることに気がついたので対応
    • これだけで10000点を超えてびっくりした
  • syusui: ローカルで動かせるようにDB用のdocker-composeを用意
  • abcang: /api/trendでは最新のisu_conditionしか必要なかったのでLIMIT 1に変更
  • abcang: /api/trendのレスポンスを1秒ほどメモリにキャッシュ
    • 後に10秒に変えてみてもまだタイムアウトしたのでN+1を解決することに決めた
  • mot: ConditionLevelをカラムに持つように変更
    • Generated Columnsは使わず、愚直にinitialize時に変換したり、挿入時にConditionLevelも追加する形に
  • このあたりでDBの構成をどうしようかという話をして、最終的にisu_conditionのINSERTが重要になってくるだろうから水平分割にチャレンジするかということになりsyusuiが着手
  • abcang: /api/trendのisuの取得部分のN+1を修正
    • ついでにisuのimageを取得しないようにカラムをしぼったりもした
    • 今考えてみると、characterの順序について考慮してなくて挙動が変わってしまっていたがここはチェック対象外だったようだ…
  • mot: isu_conditionのPOSTをgoroutineで実行する形に変更
  • mot: dropProbabilityを変えてみて、一旦0.5に落ち着く
  • abcang: /api/trend用に最新のisu_conditionを1クエリで引けるようにする
    • 最新のisu_conditionを保持するテーブルを新しく作って、isu_condition追加時に更新するようにした
    • しかし、isu_conditionの追加時の処理がUPSERTではなくUPDATEになっていたため、新しいisuについて更新されない形になってしまった
    • 点数は上がったけどユーザーが増えなくなったぞ…?という話をしていた
  • mot: appとDBのインスタンスを分離してそれぞれ1台で動く形に変更
  • abcang: generateIsuGraphResponseがすべてのデータを取得していたので、特定の日付に絞って取得する形に変更
  • mot: 静的ファイルをnginxから返すように変更
  • abcang: UPSERTになってないことをsyusuiに指摘してもらって修正する
    • これによりユーザーは増えるようになったけど、スコアが全然出なくなってしまう
    • 仕方ないのでUPSERT処理をやめて、/api/trendは初期状態から変わらないようにした
  • syusui: 水平分割の準備ができたのでマージ
  • syusui: しかし整合性チェックに通らなくなってしまい、原因を探すも時間が迫ってきて結局諦めることに
    • なので最終的に構成はnginx+appが1台とDB1が台の合計2台
  • abcang: /api/trendは初期状態から変えないことにしたのでMax-Ageのレスポンスヘッダーを設定ようにした
    • よく考えたらメモリ側のキャッシュ時間も伸ばしておけばよかった
  • 最終的に最高スコアの108634点が出て、ここでコードフリーズすることに

f:id:abcang:20210823232423p:plain
スコアのグラフ

思いついたけど結局やらなかったこと

  • icon imageの改善
    • 画像はログインユーザーが登録したものしか表示されないのでそんなに問題にならないだろうと考えていて、計測してみて問題になったらやってみようという話で手をつけなかった
    • 今考えてみるとMax-Ageぐらいつけておけばよかったかもしれない(同じユーザーが何度もアイコンを取得していたかどうかは不明だけど)
    • isuのクエリ時にimageを除外する案もあったけど、結局あまり手を出せなかった
  • created_atとupdated_atの削除
    • DEFAULT CURRENT_TIMESTAMPになっているけど、一切使われてないので消すだけで若干INSERTが早くなるかも…?と思ったけど結局やらなかった
  • 不要そうなトランザクションの削除
    • SELECTしかしてないところでトランザクションがあったりと無駄な感じはあったけど、ボトルネックではないから後でいいか…と思っていたら結局直さないままになってしまった
  • /api/isuのN+1の修正
    • 1人のユーザーが登録するisuの数はたかだか知れているだろうから直さなくていいかと判断

感想

今回はしっかり計測しつつ遅いところから順番に修正できていたと思うのでよかったです。 ただ、/api/trendは最初の状態から一切替わらずユーザーが全く増えないという事になってしまったのは少し悔しかったです。 あと、得点計算についてはややこしくて理解を諦めてしまっていたので、これについては少しまずかったかな…と若干反省です。

予選を突破できたのはチームメンバーの力があってこそなので本当に感謝です。 過去に参加した本戦ではあまりスコアを伸ばせなかったので、今度こそは本戦でいいスコアが出せるように頑張りたいです。

最後に、運営の皆様ありがとうございました。本戦でどんな問題が出題されるのか楽しみです。