ISUCON8 本戦 参加記録
2018/10/20 に開催されたISUCON8本戦に,icchy,whywaitaと一緒のチーム「時差は最大の敵」で参加しました. わたしは主にコーディングを行い,whywaitaがインフラや環境構築を行い,icchyはその両方を行うといった感じの役割分担でした.
基本的な方針として,遅そうだからやみくも直す,のではなく測定を行なってボトルネックになっているところや理論的に遅い原因になりうる箇所を直していくということを意識していました.
チームメイトが書いたブログ記事があります.
改善箇所
時系列順に改善を行なった箇所を書いていきます.
GetLatestTrade
関数にて,全件取得してから最初に1件を取り出しているクエリに,“LIMIT 1”を追加.- コードをざっと眺めた段階でこのクエリがまずそうということには気づきましたが,実際にベンチマーク時のパス毎のレスポンスタイムとMySQLのスローログをみたら確かに遅かったので修正しました.
- 外部APIの設定のキャッシュ
- 外部APIの設定を毎度MySQLから取るのではなく,変数にキャッシュするようにicchyが設定してくれました.キャッシュする場合,
inititalize
を呼ばれた時に全サーバーに反映するのが少々難しいのですが,一旦1台で動かすことで後回しにしました.
- 外部APIの設定を毎度MySQLから取るのではなく,変数にキャッシュするようにicchyが設定してくれました.キャッシュする場合,
- ユーザーのBANの実装.
- 実際のところ,これは効果があるかは良く分からなかったのですが,ベンチマーカーの挙動に依存するので実際にやってみないとどうしようもないということで早めにやってしまいました.Share機能を有効にする前に測定してしまったために効果のほどは分からず.
- 複数台構成のためにRedisとかに移すことも考えつつ,とりあえずは簡単な
sync.Mutex
を利用して実装しました.
- Share機能の有効化.
- これも良く分からなかったのですが,icchyが有効にしてみてベンチマーカーを回したところ凄い勢いでユーザーが増えて,サーバーが耐えきれずに0点になりました.
- OHLCの計算を効率良くして,キャッシュするようにする.
- Share機能を有効にした状態でのスローログのうち最悪のものがOHLCの取得・計算であったことと,リクエスト時間や回数が
info
が最大であったことから改善することを決めました.マニュアルをちゃんと読んでいなかったためにinfo
の得点がorders
の得点よりも低いことには気づいていませんでした…. - 今までのTradeのOHLCの結果がある場合,次のOHLCの計算にはそれ以降のTradeのみを見ればいいことと,過去の状態のOHLCは要求されていないことを利用して,一つのTradeに対して定数時間の処理になるように変更しました.
info
では特定の時刻以降のOHLCが要求されるので,それは二分探索で見つけるようにしました. - 改善前に
info
の中でCPUプロファイルを取ったところ,time
の関数が大きな割合を占めていることが分かりました.そこで,timeは使わずにintやstringで処理するように気を付けました. - 改善後にベンチマークを回したところロガーのエラーで0点のままでした.
- Share機能を有効にした状態でのスローログのうち最悪のものがOHLCの取得・計算であったことと,リクエスト時間や回数が
- ロガーのバルク送信の実装
- ログを直ちに送信するのではなく,5秒おきあるいは未送信のログが1MBに到達したらまとめてログを送信するように修正しました.この手の機能を実装するのにGoroutineやGochannelは非常に便利でした.
- この変更を適用した時点で2万点近くまで上がりました.
info
のデータの更新を0.5秒起きに変更.- この段階でもまだ
info
が遅く,スローログを見た所,板の最も安い売り注文と最も高い買い注文の取得のクエリに時間がかかっているので改善することにしました. - 結果をキャッシュし,注文のたびに更新するというのも考えましたが,トランザクションもいるため,キャッシュとDB間で厳密な同期を取るのに事故を起こしそうだったため諦めました.マニュアルを読んだところ
/info
は1秒遅れが許容されるとのことだったので,0.25秒起きにデータを取得してキャッシュすることにしました.OHLCの更新も一定間隔に変更しました. - この段階で学生としての入賞は行けるんじゃないかという感じでした.Failによる死亡を避けるために安定性の向上を意識し,インフラ側では再起動しても正しく動くかの確認などを始めました.
- この段階でもまだ
- Go側でのMySQL接続数の制限
- ベンチマーク毎にエラーが変動して,Failしたり得点が乱高下することに悩まされていました.エラーを見るとMySQLの同時接続数のエラーが良く出ているようでした.
- DB側のCPUで詰まっている現状アプリ側で過去のクエリが終わっていないのに接続数を増やしていくのが問題ではないかと考え,Go側で最大接続数を設定したところ,大幅に改善されました.改善理由に関して正しいかどうかはちょっと自信がありません.
反省点
- マニュアルは精読する.実装側はマニュアルを読むのに30分使っても良いかもしれません.今回
info
とorders
の得点についても気づけずに改善箇所を間違えてしまったというのは痛いです. - 言語にメンバー全員が事前に慣れておくこと.やっぱり言語に慣れていて特性やベストプラティクス等を知っているかどうかは改善に大きく影響するので.
感想
非常にやりごたえのあって楽しい問題でした.問題の規模感は時間に対して適正で,改善できそうな箇所はたくさんあるけど時間が足りず,とても悩まされました.
優勝2は完全に幸運によるものでした.問題のサーバーの機能が幸運にも知識のある範囲から出てきてやりやすかったのに,話を聞いた感じチーム「竹田氏」はわたしたちの大きく上を行っていました.
賞金ですが,ISUCON予選中に割ってしまったモバイルディスプレイの代替品の購入と,新しいノートPCの購入に活用させて頂こうと思っています.