雑記帳

情報を自分の言葉で蓄積しておく場所

ISUCON11予選に参加した(47995点で予選落ち)

要約

2021/8/21 に開催されたISUCON11オンライン予選に参加した。
結果は47995点で598チーム中79位くらい。
初参加かつほぼノー練習の状態にもかかわらず、一応目標にしていた100位以内に入れたのでOK?と思いつつ、他の参加者の参加エントリを見ているとだんだん悔しくなってきた!

isucon.net

ISUCON11の概要はこちら。

初参加のISUCON

随分前から存在は知っており興味はあったが、休みの日に8時間も拘束されるのか~疲れそうだな~と思い敬遠していたISUCON。(同じ理由で競プロにも興味はあるがコンテスト出場はしたことがない)
社のslackで#club-isucon というチャンネルがあり、わいわいして楽しそうだったのと、かつ会社で同じチームの @budougumi0617 さんも初参加されるとのことだったので、申込み人数の上限に達する直前にすべりこみ申し込みをした。
Speed of Sound というチームを組んだ。当日知ったがPerfumeの曲名らしい。⊿は昔よく聴いてたはずなのに気づきもしなかった・・・

予選までの練習

予選1ヶ月くらい前に過去問の中でも特に良問といわれているISUCON9予選問題を解こうと matsuuさんが公開してくださっていたAMIを使ってインスタンスの立ち上げ、ベンチマークの実行まではやった。
しかし公私(特に公)が忙しすぎて平日夜や休日に練習をする体力が0になり、結局きちんとチューニングをする練習をすることなく予選を迎えることになってしまった。なので実質無練習。
github.com

@budougumi0617 さんは事前に競技序盤の初速を上げるためのさまざまなチートシートを作成してくださっており、もう当日序盤の諸々はおまかせすることにしつつ、ボトルネック解消してスコアに直接コミットできるようにするぞ~~~~~と祈りながら前日は寝た。
今考えると完全に他人任せ・・・すいませんでした・・・。
github.com

当日

当日の役割分担は
- 仕様やレギュレーション把握。ログやコードを眺めつつ怪しいところをガンガン潰す人
- インスタンス上の諸々の設定。

最終的な構成

instance1: アプリケーションサーバ
instance2: DBサーバ(mariaDB)
instance3: 特に触らず

言語はもちろんGoを選択。

isucon.net やっぱGoすごいな

やったこと

  • ひたすらindexを貼った
CREATE INDEX id_isu_condition_1 on isu_condition(jia_isu_uuid);
CREATE INDEX id_isu_condition_2 on isu_condition(jia_isu_uuid,`timestamp`);

CREATE INDEX id_isu_1 on isu(jia_isu_uuid);
CREATE INDEX id_isu_2 on isu(jia_user_id, jia_isu_uuid);
CREATE INDEX id_user_1 on user(jia_user_id);

isu_conditionのindexはスロークエリログを眺めていて怪しそうだなと思ったので貼った。これで2万点くらい上がった記憶。
isuやuserへのindexも貼ったがそもそもレコード数がそこまで多くないので数千点程度の上昇にとどまった。もしかしたら他のところを直していくとだんだんここのindexが効いてきたりしたのかな?と思いつつ、なんとも言えない。

  • bulk insert
    postIsuconditionが叩かれる回数も多いし地味に遅いよな~と思いコードを眺めていたら、↓のあたりってBulkInsertできるんじゃないか?と気づき導入。1~2万点くらい上がった記憶。
    github.com

直前の業務内でsqlxのNamedExecってbulkInsertできるんだ~と気づいた直後だったので運が良かった。

  • instance2をDB専用サーバに
    mariaDBのCPU利用率がそれなりにあることに気づいたので、アプリケーション用サーバとDB用サーバとして分けた。これでまた1万点前後上がった記憶。

うまくいったこと

  • 諸々の設定をシュッと準備するスクリプト群の事前準備
    • これがなかったら午前中まるまる潰してたと思う。 @budougumi0617 さんに感謝
  • 役割分担
    • 先に↑の設定で最低限のログは見れるようになったので、自分がスロークエリログやアクセスログを眺めて当たりをつけ、シュッとインデックスやコード修正を行い、昼過ぎにだんだんスコアを上げられるようになっていた。
  • デプロイや設定類の管理をいい意味で雑に
    • 2人での参加だったので、デプロイと負荷走行の実施は声掛けで行った。特に大きな問題が発生することはなかった。
    • my.cnfのエイリアスがうまく効かない問題が発生したが、 早々にgit管理を諦める選択を取ることができた。とにかく速くできればよいいう判断基準に則って取捨選択できたのかなと。

うまくいかなかったこと

  • 修正内容と負荷走行の結果の管理
    • 修正はPRを都度作って、負荷走行を実施したら結果のURLをPRに貼り付けていた。競技終了後に見れるとは気づかず、結局どの手で何点上がったのか詳細はわからなくなってしまった・・・(上で~記憶。といってるのはそのせい)
  • ログを眺めすぎた
    • newRelicなどをつかって詳細なログを出せるのであれば、ログをじっくり眺めて修正箇所を検討するのはありだと思う。しかしそこまで詳細なログを出せないとなると、ある程度ログをみて当たりをつけた後はコードを眺めて仮説を立て、ガンガン直し、負荷走行をしたりまたログを眺めて仮説の検証をするほうが効率良かったのかなという反省。
  • テーブルの構造やレコードの中身にまで思いを馳せることができなかった
    • 他の参加者の参加エントリを見ていると、テーブルの構造やレコードの中身にまで手を入れている人も見受けられた。今振り返ると、過去問でも似たような解法があったしアイデアとしては出てくるはずだよなと思いつつも、競技中は思いつきもしなかったので、やはりきちんと素振りをしていざというときにすぐ自分の引き出しから取り出せるようにしておかないと意味ないよな・・・という反省。オンメモリキャッシュとかすぐできるじゃん・・・と思いつつできなかったのがとても悔しい。
  • nginx周りの知識、経験不足
    • 静的ファイルや、一部レスポンスをキャッシュして返却するやり方を全く知らなかったので、やったほうがいいんだろうなあとは思いつつ0から調べるコストと天秤にかけて着手しなかった。他の参加エントリを見ていると、このあたりの対応でさらに数万点あがったという人もいたので、これも自分の引き出しから取り出せるようにしておけばよかったなあという反省。

まとめ

事前準備は何もできなかったが、とりあえずスロークエリログやアクセスログをみて、index貼ったりbulk insert使ってスコアアップに寄与できたので良かった。
上記のようなことができれば初参加でも100位以内に食い込めるんだなと思いつつ、それ以上を目指そうと思うともっといろいろな武器を持っていないと上位入賞はまだまだ遠いな・・・と悔しい限りだった。
getTrend関数やpostIsucondition関数など、明らかにコードスメルが漂う場所はなんとなく察知できるものの、じゃあそこからどう直す?というアイデアをもっと出せるように来年に向けて素振りしていきたい。
dropProbabilityはこれ消したら一気に点数上がるんだろうな~と思いつつ、どこを直していくとこれを消せるのか最後までわからなかったのがめちゃくちゃ悔しい。
来年も参加するぞ!!