ばとらの部屋

ISUCON14に参加しました(batora視点)

作成日 2024年12月8日 / 最終更新日 2024年12月9日

ISUCON14に参加しました

今回、初めてISUCONに参加しました。チーム名は「Maxipus」で、サークルメンバーのyukikamome316と、nakamuraitsukiと共に参加しました。

プログラミングサークル「Maximum」からは、他にも「Maxif.」「Maximum-Y.N.K.S」が参加しました。

対策

過去のISUCONの問題を題材にして、Maximumサークル内で部内練習会を数回行いました。サークル内で得点の計測用レンタルサーバーを用意し、ベンチマーク測定&部内のLeaderboardを用いて本番形式で行いました。

こんな感じ↓

isucon practice

私は、Goを使ったバックエンドの開発は少ししか経験がなく、インフラ周りの知識もない状態からのスタートでした。部内練習会を通して、pprofを使ったり、スロークエリログを取って問題のあるクエリを見つけ、インデックスを張ったり、N+1問題を解消したりバルクインサートを使ったりと、いろいろな対策を学びました。

当日

当日は私の家に集合し、3人でパソコンを広げて準備をしました。私とnakamuraitsukiが初参加だったこともあり、本番で何かあったときにすぐに話し合って対応できるように対面で作業することにしました。

ライブ配信を見て、出題内容をチェック!

https://youtu.be/UFlcAUvWvrY?si=dySfPFYmOh55Vaam

ISURIDE...面白そう!ワクワクといった感じでした(笑)

競技開始 ~ 11:30

まずは、競技開始と共に全員がssh接続できることを確認!(←まずここからです)

私はまず簡単そうなデバックモード無効化に取り掛かろうとしたものの...無えなぁ

nakamuraitsukiがpprofを導入し、ベンチマークを取って遅そうなところを探しました。

足早にyukikamome316がスロークエリにインデックスを張り、私はコードを眺めながら怪しそうなところを探しました。

得点:910→2922

11:30 ~ 13:30

yukikamome316がgetChairStatusのN+1を解消に取り掛りました。

その間にスロークエリログからownerGetChairs関数内のクソデカクエリが遅いことが分かり取り掛かりました。改善策が分からずGPTに投げたが、それでも点数は伸びず断念。

次に、遅そうなchairGetNotification関数を見るとインデックスが張られているにもかかわらず遅いことがわかりました。nakamuraitsukiとクエリを一つ一つ眺めましたが、なぜ遅いのかが分かりませんでした。そこで、SELECT * FROM users WHERE id = ? FOR SHAREから共有ロックがかけられていることでトランザクションの待ち時間による遅延が発生しているのではないかと考え一旦消してみましたが、結局点数は伸びず断念。

得点:2922→3050

13:30 ~ 14:30

yukikamome316がサーバー分割を行おうと試みるもうまくいかず停滞。

14:30 ~ 15:30

やはりchairGetNotificationが遅く、ride_statusesを取得するのに冗長なクエリがあるように見え、テーブルの結合などを試みましたが、結局点数は伸びず。

// rides と ride_statuses を結合して最新の状態を取得
query := `
    SELECT rs.id, rs.ride_id, rs.status
    FROM rides AS r
    INNER JOIN ride_statuses AS rs ON r.id = rs.ride_id
    WHERE r.chair_id = ? AND rs.chair_sent_at IS NULL
    ORDER BY rs.created_at ASC
    LIMIT 1;
`

しかし、yukikamome316がchairsテーブルの複合インデックスの順序を変更、ridesにカバリングインデックスを貼ったことで点数が伸びました。

得点:3050→3244

16:00 ~ 17:00

nakamuraitsukiとコードをひたすら眺めていると、各クエリ自体は遅くないが別の関数から多く呼ばれていることで全体として遅くなっているのでは?と考え、キャッシュ化をしようと思いました。

手始めにchairAuthMiddlewareaccess_token, usermapで保持するようにキャッシュ化してみました。

var appUserCache = cache.NewWriteHeavyCache[string, *User]() // キャッシュをグローバルで共有
 
// ミドルウェア関数の修正
func appAuthMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		c, err := r.Cookie("app_session")
 
		if errors.Is(err, http.ErrNoCookie) || c.Value == "" {
			writeError(w, http.StatusUnauthorized, errors.New("app_session cookie is required"))
			return
		}
 
		// Cookieの値を取得
		accessToken := c.Value
 
		// キャッシュからユーザー情報を取得
		user, found := appUserCache.Get(accessToken)
		if !found {
			// キャッシュにデータがなければデータベースから取得
			user = &User{}
			err = db.GetContext(ctx, user, "SELECT * FROM users WHERE access_token = ?", accessToken)
			if err != nil {
				if errors.Is(err, sql.ErrNoRows) {
					writeError(w, http.StatusUnauthorized, errors.New("invalid access token"))
					return
				}
				writeError(w, http.StatusInternalServerError, err)
				return
			}
 
			// キャッシュに保存(TTLは設定しない)
			appUserCache.Set(accessToken, user)
		}
 
		// コンテキストにユーザー情報を設定
		ctx = context.WithValue(ctx, "user", user)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

この実装をchairAuthMiddlewareにも適応して測定したところ、得点が伸びました!

得点:3244→4027

17:00 ~ 18:00

あれこれ細かいことを試してみようと思うが、残り時間が少なく実装→ベンチ測定までできない可能性があるためログ消しに移行。

ログを消したが何故か点数が伸びなかった...しまいには3000点台に戻ってしまった。

結局最後のベンチマーク測定では得点が3546点で終了しました。

今回のISUCON14のリポジトリはこちらに公開しています。

結果

20時でのライブ配信による結果発表で、同サークルの「Maxif.」が総合29位で入賞!となりましたが、Maxipusは21時ごろに公開された順位では総合423位、学生順位50位という結果でした。

この順位は、レギュレーション違反による順位の変動前の結果なので目安といった感じです。

result

追記(12/9):https://isucon.net/archives/58837992.html で正式に結果が発表されました

最終的にはレギュ落ちしていました...なんでだろう(Maximum-Y.N.K.Sもレギュ落ちしていましたね...)

最後のenvcheckもやったし、ちゃんと計測もできてたのになぁ...再起動試験で落ちるってなんなんですかねー...

少しモヤっとした気持ちで終わってしまいました。

反省と感想

競技終了後、一部のMaxif.メンバー、Maximum-Y.N.K.Sメンバーと合流し、打ち上げ(?)をしました。

我々のチームでの反省点としては、

  • 全体的に知識不足だった(特に自分)
  • スロークエリばかりに気を取られてしまっていた
  • ISURIDEのサービスの概要について理解が浅かった(例えばISUとのマッチングの最適化(ヒューリスティック的な思考)があったことも後から知った)

良かった点としては、

  • ISUCONに初参加したことで今後の課題が見え、モチベにつながった
  • せっかく先輩方が高得点を叩き出しているのでいろいろ享受したい
  • 対面で競技を行ったことで、コミュニケーションが取りやすかった

こんな感じかな~来年に向けてバックエンドでのインターンにも興味が沸いてきたので挑戦してみたいなと思いました。まあ今回の問題に関しては、競プロ的な思考が求められる部分もあったため、幅広く知識を増やしておかなければなと感じました。