[参加記] ISUCON14 にチーム nanase として参加しました
はじめに
ISUCON14 にチーム nanase (2 名) で参加しました。
メンバ構成は以下の通りです。
- ganyariya
- 自分
- おもにインフラ関連のセットアップを担当
- あとはクエリにインデックス貼ったり、 Go のコードを雰囲気で書いたり
- 大学時代の友人
- ここ最近帰国したため、ベトナムから参加
- たまに停電になるらしく、ひやひやしながら出ていた
- おもにクエリやアプリの改善を担当
結果としては 9805 点 & 148 位でした。
想定以上より高い結果になりましたが、 安全に自分たちでできる改善ポイントを少しずつ潰す
方針を取ったからかなと思います。
一方で他チームがやっているような難しいことはやっていないため、そちらについては記事の後半でまとめて来年の課題にしたいです。
事前準備
過去問練習
ganyariya は 2022 以来 2 回目の参加であり、友人は今回がはじめての参加でした。
そのため、過去の ISUCON を各自解いたうえで都合のつく土日に集まって練習しますか、と相談していました。
しかし、お互いに私生活と仕事が忙しく、過去の ISUCON の各自練習はなしになりました。
また、通し練習は 2 日だけになり、 ISUCON13 本戦の問題を一緒に解いて「むずかしいね」になっていました。
このままだとまずいため、 ISUCON 本を全部通しで読む & 手を動かして知識だけは少しあるよという状態をつくりました。
Ansible
ISUCON13 本戦の問題を解いたとき、自分がつくった簡素なスクリプトでデプロイやベンチマークを動かしていました。
しかし、ログを計測し忘れたり、 go app のビルドを忘れていたりと処理が煩雑でした。
そのため、これらの作業を素早くもれなく行えるように Ansible スクリプトを用意して以下が行えるようにしました。
- セットアップ
- alp, slp, rewrelic などの各種ツールを ec2 3 台に配置する
- git でログインする
- 自分と友人の共通リポジトリのアクセストークンを発行して、そのトークンで gh auth login する
- before_bench
- ベンチマークを回す前にログをバックアップする
- go app を build する
- 各種 service を restart する
- after_bench
- alp, slp などの分析ツールのログを出す
こちらの ansible スクリプトを用意したことで、ベンチマークを回して分析する、が効率的におこなえてとてもよかったです。
一方でスクリプトの構成が Ansible のベストプラクティスに則っていなかったり、ログの可視化などは行えていませんでした。
そのため、来年は Ansible の構成を改善し、ログを Web 上で見れるように改善したいなと思います。
当日について
初動として ganyariya が Ansible のセットアップを行っていました。
その間友人には仕様書の README を読み込んでいただいて、改善できる箇所の下調べしてもらっていました。
Ansible によるセットアップが終わったら一緒に改善できるところからやっていました。
チームによってはアプリ・データベース・インフラのように担当を分けると思いますが、お互いにメイン分野がないため気付いたところを一緒に修正していました。
できた改善について
- notification の間隔を広げる(30ms → 2000ms)
- notification が重すぎたのでいったん間隔を広げる
- スロークエリにインデックスを貼る
- 無駄なクエリ取得している箇所をやめる
- ライドのマッチング間隔を狭める(0.5s → 0.05s)
- notification の間隔を狭める(2000ms → 1000ms)
- △ マッチングアルゴリズムを修正する
- スコアが変わらず
とにかくインデックスを貼っていました。
重いクエリの箇所があったらそこを潰す、コードを修正して必要最低限だけ取る、を行っていました。
終了 1 時間前にマッチングアルゴリズムを修正したのですが、スコアが改善せず...。
なにがだめだったのかいまだに検討がついていないです。
できなかった改善について
DB 分割
DB サーバを分割したい気持ちがあったのですが、やったことがなかったため安全のために控えました。
これは次回までに覚えたいです。
マッチングアルゴリズムの回収
「一番近い椅子を配置する」というクエリとコードを書いたのですが、うまく動作せずスコアが伸びませんでした。
マッチング間隔や notification の間隔が長かったため、そのせいで「椅子がすぐ配置される」という判定がされなかったのかなと考えています。
また、 1 回の notification API で複数人に椅子を割り当てる、というのもコードとクエリが書けずできなかったのも反省です。
SSE
notification 通知を軽量化するために SSE
を使ってもよい、という仕様があり、これを実装するか悩んでやめました。
おそらく今の自分たちの実装においては「マッチングアルゴリズムを改善したほうがスコアはあがる」という判断をして、二人でそちらを優先していたためです。
マッチングアルゴリズムを改善したとしても notification 通知が重いため、作業を分担してどちらかがどちらかをやる、にしてもよかったのかなと反省です。
決済マイクロサービスまわり
クリティカルなレベルではスコアにかかわらない、と考えていたため完全に意識していなかったです。
マッチングアルゴリズムや SSE が競技時間中に行えたら決済マイクロサービスまわりも見ていたとは思いましたが、自分たち2人には到底むりでした。
来年にむけてやること(できるようになるべきこと)
Go
とにかく Go を書けないのが一番のボトルネックでした。
ganyariya が仕事で使っているのが php であり、友人は java でした。
間を取る & もっとも使われている Go にしようか、と相談して Go にしましたがいかんせんほぼ書いたことがなく。
transaction や複数レコードの取得など、既存コードを真似しながらなんとかビルドを通す、という状態でした。
そのため、来年は Go でデータベースが絡むウェブアプリを作成して、もっとすらすら Go が書けるようになりたいなと考えています。
競技プログラミングで Go は使っていましたが、 DB などが絡んでくると全然違いますね...。
サーバ/ミドルウェア分割
アプリサーバと MySQL サーバの分割、 nginx の分割が行えなえませんでした。
そのため、安全に行えるようになるのが来年の課題です。
自動化はおそらくできないため、ドキュメントにまとめて丁寧に行えるようにしたいですね。
pprof
Go のプロファイリングツールである pprof を使わなかったため来年は使います。
ログ
Go におけるログの書き方がわからず、がんばってコードだけで理解していました。
ロガーをちゃんとセットアップして、ここのコードでなにが行われているのかを把握できるようにしたいです。
newrelic or grafana
newrelic を ansible のセットアップスクリプトで用意したのですが、結局なににも使いませんでした。
MySQL のスロークエリの分析や nginx のアクセス頻度分析も行えるはずなので来年までに詳しくなって活用したいです。
もしくはちゃんと grafana cloud でダッシュボードをセットアップして、 prometheus を各ノードにいれるだけで分析できるようにしたいです。
ansible のベストプラクティスに沿わせる
ansible の構成、とくに変数まわりがベストプラクティスに沿っていないため修正したいです。
自宅サーバの構築を手動で1台おこなったのですが、もう1台購入して ansible でセットアップしようかな。
テーブルのリレーションを図でまとめる
mysql のなかに入ってテーブルのアーキテクチャを見ながらクエリを改善しているときにこれつらいな...という気持ちになりました。
そのため、来年の ISUCON ではテーブルのリレーションを図にして吐き出すツールをなにかしら導入して、直感的に外部キーを理解しながらコードを書きたいです。
最後に
2 年ぶりの ISUCON でしたがとても楽しかったです。
運営の方々、スポンサーの方々、開催いただき本当にありがとうございました。
いろいろと不足しているところがわかったため、 ISUCON 14, 13, 12 と過去問を解いてノウハウと知識をつけていこうとおもいます。
Discussion