Y

ISUCON12予選の復習をしました 4

この記事は何か?

ISUCON12予選の復習記録です。

第4回はISUCON12 予選の解説 (Node.jsでSQLiteのまま10万点行く方法)の「3. アトミック書き込みのためのflockトランザクションに変更する」を試します。

目次

  1. 「4. アトミック書き込みのためのflockトランザクションに変更する」を試す

1. 「4. アトミック書き込みのためのflockトランザクションに変更する」を試す

flockは何のためにあったかというと、Score APICSV入稿でDELETEしてINSERTするという処理をアトミックにするためになぜかトランザクションをしらない人が作った、という設定でした。

トランザクション張らずにflockを外すと、ベンチマーカーの整合性チェックフェーズで「同じscoreを2回入稿して2回目の入稿と同時にRanking APIを叩きまくっていて、DELETEとINSERTのスキマで99行くるはずのところがそれ未満しかこなくて「アトミックになってないぞ!」とエラーにするチェックが走っていて引っかかります。

ともあれ、Scoreの追加をBulk INSERTでやるようになってクエリ数も減ったことですし、DELETE-INSERTの部分をトランザクションにしてflockを外しましょう。残りの箇所はすべて読み取りのみなので、単純に外してOKです。

ということで、flock部分をコメントアウトしました。

@@ -588,11 +588,13 @@ func billingReportByCompetition(ctx context.Context, tenantDB dbOrTx, tenantID i
        }

        // player_scoreを読んでいるときに更新が走ると不整合が起こるのでロックを取得する
+       /*
        fl, err := flockByTenantID(tenantID)
        if err != nil {
                return nil, fmt.Errorf("error flockByTenantID: %w", err)
        }
        defer fl.Close()
+        */

        // スコアを登録した参加者のIDを取得する
        scoredPlayerIDs := []string{}
@@ -1078,11 +1080,13 @@ func competitionScoreHandler(c echo.Context) error {
        }

        // / DELETEしたタイミングで参照が来ると空っぽのランキングになるのでロックする
+       /*
        fl, err := flockByTenantID(v.tenantID)
        if err != nil {
                return fmt.Errorf("error flockByTenantID: %w", err)
        }
        defer fl.Close()
+       */
        var rowNum int64
        playerScoreRows := []PlayerScoreRow{}
        for {
@@ -1291,11 +1295,13 @@ func playerHandler(c echo.Context) error {
        }

        // player_scoreを読んでいるときに更新が走ると不整合が起こるのでロックを取得する
+       /*
        fl, err := flockByTenantID(v.tenantID)
        if err != nil {
                return fmt.Errorf("error flockByTenantID: %w", err)
        }
        defer fl.Close()
+       */
        pss := make([]PlayerScoreRow, 0, len(cs))
        for _, c := range cs {
                ps := PlayerScoreRow{}
@@ -1419,11 +1425,13 @@ func competitionRankingHandler(c echo.Context) error {
        }

        // player_scoreを読んでいるときに更新が走ると不整合が起こるのでロックを取得する
+       /*
        fl, err := flockByTenantID(v.tenantID)
        if err != nil {
                return fmt.Errorf("error flockByTenantID: %w", err)
        }
        defer fl.Close()
+       */
        pss := []PlayerScoreJoinPlayerRow{}
        if err := tenantDB.SelectContext(
  • 変更前のスコア
22:05:24.438783 SCORE: 3862 (+3862 0(0%))
  • 変更後のスコア
22:04:15.150282 SCORE: 6058 (+6058 0(0%))

スコアが2,196改善しました。

感想

flockは明らかに「変な」実装なので、その違和感に瞬時に気づくべきでした。

Go言語の実装力、読む速さ、慣れが足りないのが原因だと思います。それとDBのロックについても詳しくなりたいです。

変な実装は一旦コメントアウトしてベンチの結果を見てみるなどするのも良さそうです。

スコアは改善しましたが、たまにベンチが失敗する場合があるようで、このあたりは今後確認します。

参考

1 ISUCON12 予選の解説 (Node.jsでSQLiteのまま10万点行く方法)