2021-09-18 に開催された ISUCON11 の本選に参加してきたときのメモ。

チーム構成

チーム名

Oops!

メンバー

  • 2matzzz
  • nyaa
  • kjm

昔は同僚だったけど、いまはそれぞれ別の会社で働いているメンバー達。

選択言語

Go言語

スコア

Oops! のみのグラフ

  • Best/Latest 93,716
UTC Score
2021-09-18 01:09 31,969
2021-09-18 01:15 33,639
2021-09-18 01:25 35,112
2021-09-18 01:31 36,202
2021-09-18 01:38 35,662
2021-09-18 01:45 36,942
2021-09-18 01:56 36,154
2021-09-18 02:08 36,234
2021-09-18 02:10 36,840
2021-09-18 02:14 36,959
2021-09-18 02:21 39,465
2021-09-18 02:24 34,372
2021-09-18 02:27 39,810
2021-09-18 02:31 39,644
2021-09-18 02:33 42,591
2021-09-18 02:42 42,995
2021-09-18 02:44 43,226
2021-09-18 02:47 42,129
2021-09-18 03:07 44,199
2021-09-18 03:10 43,521
2021-09-18 03:15 44,035
2021-09-18 03:20 41,470
2021-09-18 03:22 40,860
2021-09-18 03:56 41,856
2021-09-18 04:18 43,468
2021-09-18 04:26 42,589
2021-09-18 04:28 41,903
2021-09-18 04:30 44,697
2021-09-18 04:34 43,812
2021-09-18 04:39 46,276
2021-09-18 04:42 43,960
2021-09-18 04:45 43,922
2021-09-18 05:00 44,102
2021-09-18 05:03 45,519
2021-09-18 05:05 44,340
2021-09-18 05:11 45,423
2021-09-18 05:17 45,323
2021-09-18 05:20 45,420
2021-09-18 05:36 43,768
2021-09-18 05:41 46,432
2021-09-18 05:43 46,718
2021-09-18 05:52 45,942
2021-09-18 05:58 45,169
2021-09-18 06:00 47,141
2021-09-18 06:07 69,966
2021-09-18 06:27 71,585
2021-09-18 06:31 69,999
2021-09-18 06:35 74,496
2021-09-18 06:38 69,043
2021-09-18 06:41 73,200
2021-09-18 06:56 69,506
2021-09-18 06:59 71,532
2021-09-18 07:02 72,975
2021-09-18 07:05 76,300
2021-09-18 07:35 92,244
2021-09-18 07:39 37,551
2021-09-18 07:47 42,048
2021-09-18 07:50 87,903
2021-09-18 08:07 81,574
2021-09-18 08:09 85,563
2021-09-18 08:13 91,592
2021-09-18 08:20 84,248
2021-09-18 08:24 81,615
2021-09-18 08:27 79,610
2021-09-18 08:29 90,119
2021-09-18 08:33 91,624
2021-09-18 08:41 51,328
2021-09-18 08:42 87,154
2021-09-18 08:46 90,508
2021-09-18 08:47 93,608
2021-09-18 08:52 92,235
2021-09-18 08:54 93,716

結果

(85,666) Oops! (提出課題のダウンロードが404)

ブラウザ動作確認失敗で失格でした!

TVer賞 新しいベストスコア - 今までのベストスコア が大きい4チームにChromecast With Google TVを人数分。 Oops! (22,825点差を更新)

TVer賞ありがとうございます!

最終的にチームでやったこと

10:00 - 最終スコア 36,154

  • アプリケーションのビルド、ログのローテーション、サービスの再起動を行う Makefile を書いた
  • #1 MySQL interpolateParams を true に設定(33,639)
  • #2 AddAnnouncement(): UnreadAnnouncement の追加が複数のINSERT文だったのを一件のINSERT文にまとめる(36,942)

11:00 - 最終スコア 42,129

  • #3 courses.status, unread_announcements.is_deleted にインデックスを追加
  • #3 WHERE courses.status !=WHERE courses.status IN() に変更(36,840)
  • #4 createSubmissionsZip(): 外部コマンドを起動していたのをGoネイティブでZipファイルを生成する(39,565)
  • #5 echo frameworkのログ出力を停止(42,691)

12:00 - 最終スコア 41,856

  • #6, #7 SubmitAssignment(): io.ReadAll() + os.WriteFile() を io.Copy() に書き換え(44,199)
  • #10 archive/zip を github.com/klauspost/compress に変更(44,847)
  • #13 GetCourseStatus(): コースの存在確認で SELECT COUNT(*) を実行していたのを削除し、UPDATE 文の RowAffected() の件数で確認するようにしたのとトランザクション排除(46,532)

13:00 - 最終スコア 43,922

  • #9 GPAの統計値をSingleflightで処理する(46,376)

14:00 - 最終スコア 45,169

  • #12 GetClasses() のトランザクションを排除(45,423)

15:00 - 最終スコア 71,532

  • #8 GetAnnouncementList()の改善
    • LIMIT ? OFFSET ? を排除し announcements.id でページング()
    • unread_announcements テーブルの user_id, is_deleted にインデックスを追加
  • #14 DownloadSubmittedAssignments() のトランザクションを排除(71,785)

16:00 - 最終スコア 87,903

  • #15 db.SetMaxOpenConns() のチューニング (92,244)

17:00 - 最終スコア 93.716

  • MySQL InnoDBの秘伝のたれ
  • nginx の秘伝のたれ
  • 再起動試験

2021-09-20追記: ブラウザ動作確認失敗に関して

(85,666) Oops! (提出課題のダウンロードが404)

提出課題のダウンロードというのは以下のEndpointだと思われます

  • GET /api/courses/:courseID/classes/:classID/assignments/export 提出済みの課題ファイルをzip形式で一括ダウンロード

Goのアプリ内の関数でいうと DownloadSubmittedAssignments() です。 今回、3つほど修正を加えていたのですが、最後に行なった以下の修正に問題があったと思われます。

  • #14 DownloadSubmittedAssignments() のトランザクションを排除(71,785)

この修正のうち Class が存在しているかのチェックする処理に問題がありました。

if result, err := h.DB.Exec("UPDATE `classes` SET `submission_closed` = true WHERE `id` = ?", classID); err != nil {
  c.Logger().Error(err)
  return c.NoContent(http.StatusInternalServerError)
} else if classCount, _ := result.RowsAffected(); classCount == 0 {
  return c.String(http.StatusNotFound, "No such class.")
}

SELECT COUNT(*) の代わりに、UPDATE できた行数が0の場合にClassが存在しないという扱いにしておりました。 2度アクセスすると、既にClassの submission_closedtrue に設定されているため result.RowsAffected() は0になってしまい、 404がレスポンスされてしまう結果となりました。

ベンチマーカーでは1度しかアクセスしないため、Failにならずに済んでいたのだと思われます。 最終的にブラウザで試験を行うという重要なことをやっていなかったのが問題であったと反省しております。

また、二度目の UPDATE の際は0行になるという点に関して仕様をきちんと把握できていなかったため、このようなコードを書いてしまいました。 ひとつ勉強になりました。