Techtouch Developers Blog

テックタッチ株式会社の開発チームによるテックブログです

ISUCON 11 振り返り

isucon11

この記事について

ISUCON 2021 予選に参加したときの成果と答え合わせをして振り返るというものです。結果だけ書いてしまうと予選敗退でした。10万の壁は超えられず。言語は弊社でも使っていて慣れている Go にしました。

f:id:techtouch:20211112164707p:plain

↑最終スコア(こちらより)。個人的に前回よりはまともに戦えて少し成長を感じました。

ちなみにISUCONとは、お題となるWebサービスを決められたレギュレーションの中で限界まで高速化を図るチューニングバトルのことです(公式より引用) 今回の出題は大雑把にいうと椅子のセンサーデータをうまく扱えるようにチューニングしていこうというものでした。

当日作業内容

デプロイ周りの整備

  • github にソースコード持ってくる
  • deploy script 作成
$ make deploy

でできるようにしました。下記Makefileです。一応サーバ起動時に意図したcommit hash で動かせているか確認できるように go のソースコード側に変数渡しています。

REVISION=$(shell git rev-parse --short HEAD)

build:
    GOARCH="amd64" GOOS="linux" go build -trimpath -ldflags "-X main.revision=$(REVISION)"

deploy: build
    ssh isucon@<ec2-ip> sudo systemctl stop isucondition.go.service
    scp -P 22 isucondition isucon@<ec2-ip>:/home/isucon/webapp/go
    ssh isucon@<ec2-ip> sudo systemctl start isucondition.go.service
    ssh isucon@<ec2-ip> sudo systemctl enable isucondition.go.service

APMツールの設定

  • NewRelic のセットアップ
    • 各サーバに agent install
    • go application に agent セットアップ

NewRelic dashboard でこんな感じでモニタリング可能になります

API Request のモニタリング f:id:techtouch:20211115125756p:plain

コード上の計測したい処理単位(クエリ発行など)で詳細にパフォーマンスログを取る設定も下記のように設定していきました。ただし、うまく共通化しておらず、コード上で逐一書いていったためもう少しこの作業を早くしたかったです。

f:id:techtouch:20211115141702p:plain

こんな風にみることができます。

f:id:techtouch:20211115141427p:plain

MySQL のモニタリング f:id:techtouch:20211115125817p:plain

  • モニタリング結果・アプリケーションからのクエリから Index の設定
alter table isu_condition add index isu_condition_jia_isu_uuid(jia_isu_uuid);
alter table isu_condition add index isu_condition_jira_isu_uuid_timestamp(jia_isu_uuid, timestamp);
alter table isu add index isu_jia_isu_uuid_jia_user_id(jia_isu_uuid, jia_user_id);

改修した箇所

  • N+1 になっていた trend 部分の処理改善
    • ORDER BY timestamp DESC → ORDER BY timestamp DESC limit 1
      • コードを見ると取得したデータのうち、最新しかつかっていなかったので 1件のみ取るようにした
  • isuCondition の bulk insert でスループット向上
    • リクエストされた10件ほどのデータをfor 文で個別に insert 処理していたので一括で入れ込むように修正
  • isu の性格データをハードコーディング
    • db に入っていたが、変更がない & データ少ないのでコード上に埋め込んだ
  • DB サーバ とアプリケーションサーバ分離
  • trend や isu list 時に cache 機構導入
    • userID を hash key にして、API が 200 返せたときに内部にキャッシュして以降利用する
  • isu の icon 画像が db に入っていたのでファイルから配信できるように変更
    • 初期化や新規登録時の画像はファイルに書き込んで go 側からファイル読んでバイナリ返すようにした(本当はうまく nginx でやりたかったけどできなかった)
  • NewRelic 停止

中盤で5万点近くいって、本戦ラインに入っていると喜んでいたのですが伸び悩んでしまいました。その時の課題感としては、もう少しキャッシュをうまくできたんじゃないかというのはありました。それ以外は他にどうしていくかが決まらず、問題の読み込みや、どうすればスコアがもっと上がるかを解き明かさないといけなかったです。なので目先の改善はできたものの、ベンチマークのログやスコアの算出方法から抜本的に変えるべきところを見つけるまでには至りませんでした。

公式解説記事を読んだ学び

できていなかった重要改善点

  • graph 読み取りリクエストの最適化
    • データの読み込み件数を limit で減らすことが可能。書き込み時に上記 api でやっている条件を事前に store してしまえば必要なもののみクエリできるようになる。読み込みロジックで行っているフィルタリングをクエリできるようにするには書き込みでどうにかしてあげればいい。
    • 全件取得になっていたら、区間指定できないか検討する。レギュレーションと照らし合わせれば気づけたように思える。この場合は、当日のデータが返せていればいいので区間指定できた。データがガンガン書き込まれていくので全件取得するクエリは時間に比例して重くなっていってしまう。
  • 負荷の効率的な分散
    • センサーデータの書き込みエンドポイントが選べるようになっていたことの意味に気づけず、nginx に任せて分散させてしまっていた。この仕組に気づいて Write と Read 分けてモニタリングして行けばスコアのボトルネックも気づけたかもしれない。

その他、log を止めるなどしっかりチューニングできてからの課題もありましたがクリティカルなできていなかったところは上記かなという印象でした。やはり前段で述べたとおり、出題のストーリーを読み解いてデータの依存や仕様を理解してチューニングに至らなかったのが大きな反省点かと思います。次回はコードだけに集中せず予選マニュアルとも向き合ってチューニング進めるのを意識していきます。

その他

弊社では、私のチーム以外にも2チーム参戦していて大会前後はわりとわいわいしています。業務ではフロントエンドをやっている人も参加してるのが珍しいかもしれません。私は業務でもバックエンドやっているので負けないように少しドキドキしながらの参戦になっています。

予選は8月下旬だったのにいつの間にか冷たい風が吹く季節になって、ISUCON 12 もあっという間にやってくるんだろうと楽しみにしています。次回こそ本戦に行きたい!

ISUCON のようなサーバ側のチューニング好きな人、フロントエンドのパフォーマンスチューニング好きな人、弊社のプロダクトが気になる人を絶賛採用募集中です。採用ページ 是非ご覧になってみてください!

参考