🪑

ISUCONに初参加してみた!(2024. ISUCON14) | jig.jp Engineers' Blog

2024/12/11に公開

はじめまして!せりと申します。24卒のサーバーサイドエンジニアとして、Javaによる開発をしています。 自分からはjig.jp Advent Calendar 2024の11日目をお届けします🙏

2024/12/8(日)に開催された「ISUCON14」に参加しました!今回のレポートではきっかけ・勉強・当日の雰囲気までをまとめました。

ISUCONとは

公式サイトによると、
ISUCONは「Iikanjini Speed Up Contest」の略で、『お題となるWebサービスを決められたレギュレーションの中で限界まで高速化を図るチューニングバトルです。』

ソースコードをガンガン書き換えるもよし、サーバー構成を変えるもよし、キャッシュを導入するもよし。とにかく高速化すれば正義!という内容です。

きっかけ

大学時代

通学中によく聴いていたiwashiさんによるPodcast「fukabori.fm」で、ISUCONを初めて知りました。具体的にはこちら:

このときは、「なんかチューニングして、競い合うらしい」ということだけを記憶して、入社まで時は過ぎるのでした。

入社

入社してから、「どんなSlackのチャンネルがあるかなぁ」と漁っていると「#ISUCON」があるじゃないですか!!とりあえず、潜りこんでおいたのでした。
10月になると、誰が参加する?みたいな話が出て、「今年は不参加で勉強します」と言おうと思ったのですが、先輩の「参加が一番の勉強だよね~」という言葉にしっくりきて、チーム「moudameda」に参加することにしました。

勉強

ISUCON本

本当にISUCONがわかっていないので、まずは「ISUCON 勉強」でググり、以下のサイトを見つけました。

そこでISUCON本こと「達人が教えるWebパフォーマンスチューニング 〜ISUCONから学ぶ高速化の実践」が紹介されており、ひとまずそれで勉強することにしました。
学んだこととしては、以下なのですが、いつもの業務でも使えそうなものもたくさんあり、パフォーマンスの勉強をする良い機会になりました。(早速、クエリ改善のタスクを取っちゃった勢い🙈)

  • alpによるnginxのログ分析
  • pt-query-digestを使った、MySQLのスロークエリログ分析
  • SQLのインデックスとN+1の解消
  • キャッシュの使いどころと注意点

Go

次にどの言語で、ISUCONを戦うのかを決める必要がありました。というのも、ISUCONの言語は「Go、Perl、PHP、Python、Ruby、Rust、Node.js」なので、普段使用するJavaがありません。しかし、チームメンバーに普段からGoを使っている方がいたので、「Goでいこう」という方針になりました。ただ、自分はここ1-2年ほどGoをさわっていないので人生2回目ぐらいの「 A tour of Go(日本語)」をやりました。正直一夜漬けみたいなものなので、自分は「インデックスを貼る」+「N+1の解消」を頑張ろうと割り切ることにしました。

素振り

他の勉強としてはISUNARABEprivate-isuでの素振りをしました。
個人練習のときは、レプリケーションの練習、ISUCON本の付録「private-isuの攻略実践」(とくにalpとpt-query-digest)を行うことで、ISUCONの雰囲気をつかみました。
チーム練習では、alpやpt-query-digestなどのツールの導入、GitHubリポジトリを作る流れをやり、初動をどのようにするのかを決めました。

1人目:GitHubに上げる、ツールの導入
2人目:マニュアル読む+実際に触って理解をする→問題がありそうなところに取り組む
自分:マニュアル読む+実際に触って理解をする→インデックスを貼る + N+1

当日

開始前

9:00に会社に集合。弊社の他チーム(別部屋)と「どんな問題来るかな~」と話しつつ準備をしました。
部屋の中央の画面に公式放送をつけ、「いよいよはじまるんだ!なにがくる?」という気持ちでいっぱいになりました。
9:50過ぎ、今回のISUCONのお題「ISURIDE」(問題のGitHubリポジトリ)という、ライドチェアサービスが発表されました。

開始

初動

事前の打ち合わせ通り、「自分はマニュアル+実際に触ってみる」を実行し感触をつかみました。このサービスで顧客は乗車位置と下車位置を指定するのですが、移動距離を遠く指定してしまったので「チェアが乗車位置にくる&移動時間がめっちゃ遅い(笑)」と思いつつ、サービスの把握を進めました。

ここで事件。動作を試していると急に503が出ました。環境構築のための「秘伝のたれ」を流し込んでいるときにISUCON13のリポジトリをpullしてしまい、ISUCON13が始まってしまっていたのです!チームで笑いが起き、自分は「このレポ行きのネタがきた!」と思ったのでした。

その後、まずChatGPTにソースコードを張り付け「ソースコード中でパフォーマンス課題があると推測される関数を負荷の高いものから順に列挙してください。」と分析をお願いし、結果をGitHubのWikiに貼ることで共有しました。

出力の冒頭

コード全体を分析した結果、負荷が高く、パフォーマンス課題となる可能性がある関数を負荷の高い順に列挙します。

1. appGetNearbyChairs
課題点:
椅子のリストをすべて取得し、それぞれについてライドの状態と位置を個別にクエリでチェック。
大量のデータがある場合、椅子のループ内でデータベースの複数回のクエリが発生。
影響範囲:
椅子の数が増えるほど、負荷が急増。
データベースへの頻繁なアクセスがボトルネックに。
2. getChairStats

また、DBのテーブル構造、pt-query-digestの結果を貼り付け「必要なインデックスを教えてください」と分析をさせ、インデックスを貼りました。これで+1700点でした。
これらの初動はLayerXの@y_matsuwitterさんの「ISUCON13にLLM活用担当で参戦しました」を参考にさせていただきました。

エラーログ以外を消す

自チームではローカル環境の構築をしなかったということもあり、「デバッグはどこを見るとよい?」という話題が出ました。ChatGPT君に聞くと「journalctl -u isuride-go.service | less」をすれば見れるよと教えてくれました。実際に行くと大量のリクエストのログがあり、「nginxのログか?」と勘違いしてしまいました。よくよく調べるとGoでログを作成していたので、以下のようなカスタム関数を作成して正常のログは出ないようにしました。

func errorLoggerMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// 次の処理を実行
		next.ServeHTTP(w, r)
		// レスポンスのステータスコードがエラーの場合のみログを記録
		if w, ok := w.(middleware.WrapResponseWriter); ok && w.Status() >= 400 {
			slog.Error("Request resulted in error",
				"method", r.Method,
				"url", r.URL.String(),
				"status", w.Status(),
			)
		}
	})
}

appGetNearbyChairs関数

次にChatGPTでの分析結果とalpの上位にいたappGetNearbyChairs関数のN+1の解消に手を付けました。一夜漬けではリファクタリングする能力もなくLLMを頼ったのですが、苦戦をしました。

  • リファクタリングするとデータ数が増えたときにタイムアウトする
  • LLMはPostgreSQLの形式を出すなど動かないクエリを作る
  • 関数の動作が変わっている

これらを防ぐために試行錯誤して修正できたのですが、スコアには寄与せずリバートと相成りました(トホホ)
そもそも、Goの理解のなさが露呈したのと、修正の重要性の判断とその材料集めに不足があったなと痛感しました。

レプリケーション?

16:30ごろ、「もうそろそろレプリケーションやる?」とチームで話題に挙がりました。しかし、ここでふと気づきます。「アプリケーション上でレプリケーションを振り分ける練習をしていなくね」と。初期実装で使われているSQLライブラリでレプリケーションをやるのが大変という話になり、諦めました。

終盤

ほかのメンバーは「チェアのマッチングの改善」「MySQLにgeometry型を導入することで位置計算をはやく」などをやっていたのですが、時間が間に合わず。。。最後のあがきとしてnginx、MySQL、Goのログを駆逐するという作業を行い、800点ほど加算して「4043点」でフィニッシュとなりました。(なお追試の結果を見ると、ロガーの抑制が効いたというより奇跡の乱数を引いた可能性も高い気がしてきた💦)

反省

自分の今回のISUCONの反省は以下です

  • コードと負荷に関して全体を見る余裕と練習が不足していた
  • Goの理解が不十分
  • レプリケーション自体の素振りしかしていなかった

全体的な準備不足と能力不足を実感しました。来年は「チューニングをやりきるほどの素振り」と「チームで4か月前からしっかり取り組む」ということをチームで約束して、今回のISUCONを終えました。個人的には、毎月第一土曜日は1日かけてISUCONをやる日にしたいと思います(Googleスケジュールに入れた。)

業務上の意識するようになったこと

  • SQLのクエリのコストを気にするようになった。
  • (弊社で使っている)Datadogを学ぼうというきっかけになった。

感想

「つよつよエンジニアたちの大会だぁ」と思っていたISUCONに実際に参加して、良い体験ができたとともにパフォーマンスについて勉強するきっかけになりました。ISUCONがどのようなものかを理解できたので、来年はパフォーマンスの壁になる箇所を1つはクリアできるようにしたい!と宣言して筆を置きます。

jig.jp Engineers' Blog

Discussion