💪

ISUCON12本選に向けてやることまとめてみた

2022/08/03に公開

ISUCON12予選突破しました

こんにちは.
ISUCON12予選に大学同期と参加して突破しました.
予選から1週間経って参加記としては遅くなってしまったのですが,本選でどういうことをやっていくかの整理のためにも普段練習などでの手順をこの場で整理したいと思います.(本当は怠惰で遅くなりました🙈)

普段はWantedlyでBackendエンジニアをしています.ISUCONはプロダクト改善の一部だと思っているので普段の業務の考え方にも通ずるものがあると思っています.

この記事では僕らのチームがISUCONでの作戦の手札として何を持っているかを公開します.

  • 「ISUCONに参加したいけど何をすればわからない!」
  • 「ISUCONで作戦が思いつかない!」

みたいな人におすすめです.

※ 同じような内容をWantedly Engineering Podcastで話しています!こちらもぜひ聞いてみてください!
https://anchor.fm/wantedly-dev/episodes/ISUCON--w--bubusu_ryu-e1lllp4

準備大事

まず前提として,ISUCONにおいて準備は大切ということを言っておきます.
かつてはインシデントのように突然イベントが発生してバックエンド・インフラの知識で殴り合う場というイメージを持っていたのですが,最近だとスポーツ競技のように事前に準備をして本番で本領発揮できるかが重要になってきている気がします.
もう12回も開催してきて傾向が見えてきたからとも言えるかもしれません.
毎回題材が違うとは言え,ボトルネックになりうる場所はある程度一般化できて如何にその場所を素早く見つけて潰すのかが大事かなと思います.(ISUCONの本質とは違うかもしれません)

僕らのチームは過去問を何回か解いて練習をやりつつ今のスタイルを身に付けました.
ここからは実際に何をするのかを説明していきたいと思います.

何をやるのか

概要

基本的に競技中は以下の3つのフェーズに分かれると思います

  1. 準備(1h)
  2. 改善(6.5h)
  3. 締め作業(0.5h)

カッコの中は競技時間の8hの中で,どれくらいの時間を割くかです.
ここからはフェーズごとに具体的に何をしているのかを説明していきます.適宜僕らがISUCON12予選で実際に使ったリポジトリとプルリクをリンクしておくので見てみてください.最終スコアは29024点でした(画像は点数の推移).

1. 準備フェーズ

準備は改善をスムーズにするために必要なことをやります.ここはチームの方針さえ決まっていれば題材にかかわらず用意できる部分が多いので,競技前に段取りを確認できているとベストです.

監視ツールの導入

僕らは改善の際にどこがボトルネックかをみるためにたくさんのツールを導入しています.

  • git(コード管理及びデプロイ基盤)
  • alp(アクセスログ解析)
  • alpdiff(alpの差分表示)
  • slackcat(slackにログや結果を投稿)
  • netdata(CPUやメモリ全体使用量監視)
  • Cloud Profiler(CPU使用割合内訳)
  • MySQLのSlow Query Log

たくさん入れてますね.これらを毎回手作業で行うのは辛いのでansibleで全ていい感じに設定してくれるconfigを書いておいて1コマンドで実行されるようにしています.

Makefile追加

Makefileは僕らの改善サイクルの肝です.
デプロイ,ログローテーション,ベンチマーク走行後のログ解析結果をSlackに投稿,アプリケーションログの確認などがそれぞれ1コマンドでできます.
ログ解析結果は以下のような感じでSlackに投下されます.

上から順番にアプリケーションの稼働情報,alpの結果, アプリケーションのエラーログ, alpdiffの結果, MySQLのSlow Query Logが吐かれます.
これを見れば今改善中のブランチのアプリケーションの状況が一目でわかりますね.

DB ER図

大体の場合テーブル構成を構築するSQL(CREATE TABLE)がまとまったファイルが予めあるので,そこからER図を自動生成しています.
どういうテーブルがあって,リレーションを貼っているのかがパッとわかるので最初にやっておくと後々DB分割を考える際にも楽です.
SlackにER図を貼っています

マニュアル読む

これは最も大切な作業の一つです.改善のヒントが書かれていることが多く,作戦の種になります.また,失格は最低限避けたいので失格の条件などは十分に確認します.毎回マニュアルを読みに行くのはしんどいので,重要なことはメモっておいて後から見返せるようにしています.

GitHub Project

これは単純にどういう改善のアイディアがあって,今誰が何をしているかの把握のために使っています.ツール自体は何でも良いですね.もはや3人なのでそこまでする必要もないかもしれません.

アプリとDBをサーバー分割

僕らはアプリケーションとDBのどちらがボトルネックになっているかを常に把握するために準備フェーズでサーバー分割します.アプリケーションのDBの向き先を2台目にするだけなので簡単にできると思います.

ベンチマークを回す

初期状態とスコアを確認します

2. 改善フェーズ

改善フェーズではスコアを伸ばす施策をガンガン進めます.
ここではさすがに問題によることが多いので気をつけていることと一般的な施策を紹介していきます.

気をつけていること

作戦会議

チーム内で作戦会議はよくします.8時間という短い時間でより多くの改善をするためにはリソース配分を気をつけなくてはいけません.複数人が同じ作業をしていたり,ダイレクトに効かない施策をしていたりして無駄な時間を使う暇はありません.常にチーム内で誰が何をしているかを把握し,最適な動きをします.また,煮詰まってどうしようもない時もあるのでチームで外に出て散歩に行くことも多々あります.
特に今回の予選はSQLiteをどうするのかを早めに意思決定するのが重要だったので,昼すぎにはMySQL移行しないと決めたのは英断でした.

施策は小さく

これは最も気を付けるべき事項かもしれません.ISUCONにおいて最もやってはいけないことは「沼にハマる」ことです.プログラミングをしているとあると思いますが,ケアレスミスで何十分も溶かすことがあると思いますが,競技は時間制限があるので施策がただ進めば良いと言うわけではありません.このような事故を避けるためにも施策は小さく回して大きな変更を行わないようにしましょう.

ペアプロ

僕らはよくペアプロをよくします.目的は上の事項と同じで「沼にハマる」を最小限にするためです.ペアプロは単純に2人の知識が活かされるだけでなく,やっていることが明確に言語化されます.ペアプロを始めて,実装者が説明をしている間に自分でミスに気づくということはよくあります.沼りそうと思った時はすぐにペアプロをするように意識しています.

マージ判断とユーザーストーリー

「改善をして目的のエンドポイントは速くなったけどスコアは伸びなかった」,こういうものはマージして取り組むべきか悩むポイントだと思います.安易に入れてしまうと後々悪影響があるかもしれません.僕らはそういう時の基準をalpdiffの結果を元に判断します.
alpdiffではエンドポイントごとにどれくらい速くなったかとどれくらい呼ばれるようになったかの差分がわかります.(比較対象はそのときのmaster)

ISUCONはあくまでもサービス(プロダクト)を良くするという前提のもとパフォーマンスチューニングをしています.ベンチマークの動きもその前提のもと作られているので,サービスでの体験が良くなる改善を行うと利用ユーザーが増えサービス全体が活性化するようになっています(リバースエンジニアリング的な視点).僕らはスコア自体が伸びなくてもユーザーストーリーを考えてサービスが活性化しているならばマージしても良いという判断を良くします.

良く考える施策

ここからは過去問の傾向で考えた良くある対策の一部です.盲目的にこれらをやるのは良くありませんが,参考になると嬉しいです.
基本的にはDBの負荷を下げるものとなります.

インデックスをはる

これは良くやる手法No.1ですね.ただ気をつけているのは無闇に全部のクエリを見て貼るのではなく,今改善したいクエリを改善するようにしています.これはインデックスを貼りまくってデータ追加のタイミングで遅くなるのに気づかないのは避けたいという意図があります.もちろん複数のクエリを一度に改善することもあります.これは人によって考え方が違いそうですね.

N+1問題を直す

ISUCONの問題にはN+1問題が必ずといっても良いほど出てきます.これも見つけたからすぐ飛びつくのではなく,今問題になっているエンドポイントやクエリを考えてから順番に直すようにしています.

NGINX, MySQLの秘伝のタレをいれる

ISUCONではNGINX, MySQLが大体の場合使われています(今回の予選や過去の本選では違うものが使われましたが...).これは予め競技前に用意できるので,準備したものを競技中に必要に応じて入れとくと良い改善につながると思います.

insertの処理

ISUCONでは時折大量にデータを追加する処理が入っていることがあります.今回の予選でのCSV入稿であったり,過去の本選であったIoTから大量のログデータが入ってきたりがあったりします.insertの処理は大量にあるとDBに負荷をかけることがあるのでどうにかします.以下の2つをやれば大抵改善するはずです.

  • bulk insert
  • 非同期でinsertする

特にbulk insertは自分の中ではDBの負荷を下げるのに効きやすい印象です.Goだとsqlxで簡単に書けるのでスニペットを用意しています.非同期処理に関しては当日マニュアルに反映が遅れても良いエンドポイントが書いてある場合があるので参考にすることもあります.

キャッシュ

何かのレコード数であったりレスポンス全体であったり予め計算して保存しておくことで,読み取り時の計算を少なくすることができます.
ただこれは諸刃の剣だと思っています.大体がRDBの値から計算・構築されるものであると思います.このとき問題となるのはキャッシュの更新タイミングによっては計算元の値と乖離する時間があるのでそこが許容できるかです.ここを見誤るとベンチマークの互換性チェックで落ちたり走行時にエラーが起きたりするのでキャッシュの施策をした後はログなどを注意してみるようにしてください.

3. 締め作業

締め作業は主に2つ確認します

再起動試験が競技後に行われるので,ちゃんとアプリケーションが立ち上がるか確認します.僕らは実際に再起動するのではなくsystemctlの値を確認するだけにしています.
また,監視系のログを止めるのは具体的にはNetdataやProfiler, Slow Query Logなどを止めます.これをすることでCPUリソースの負荷を下げれるので点数が上がります(今回の僕らのチームだと最後に9000点ほど上がりました)

まとめ

ここまで自分がISUCONでやる作業をつらつらと書き綴りました.ISUCONは負けても勝っても学びが多いので楽しいですね.この記事でまとめたことを実践して本選も頑張ります💪
特にWantedlyは今回プラチナスポンサーなので余計に頑張ります💪

最後に宣伝.Wantedlyでは技術的なことはもちろんのこと開発の全てをEngineering Handbookにまとめています.ISUCONでもプロダクト改善という視点では同じことをしているのでぜひ見てみてください!
https://docs.wantedly.dev

Discussion