1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

sqlcでトランザクションのロールバックエラーを適切に処理する方法

Posted at

背景

sqlcでトランザクションを使うことについて、公式ドキュメントを見ていると、少し気になるところがあったので、自分なりにコードを改善してみました。

公式のコード

func bumpCounter(ctx context.Context, db *sql.DB, queries *tutorial.Queries, id int32) error {
	tx, err := db.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()
	qtx := queries.WithTx(tx)
	r, err := qtx.GetRecord(ctx, id)
	if err != nil {
		return err
	}
	if err := qtx.UpdateRecord(ctx, tutorial.UpdateRecordParams{
		ID:      r.ID,
		Counter: r.Counter + 1,
	}); err != nil {
		return err
	}
	return tx.Commit()
}

tx.Rollback()のエラーをキャッチしたい

- func bumpCounter(ctx context.Context, db *sql.DB, queries *tutorial.Queries, id int32) error {
+ func bumpCounter(ctx context.Context, db *sql.DB, queries *tutorial.Queries, id int32) (err error) {
	tx, err := db.Begin()
	if err != nil {
		return err
	}
-	defer tx.Rollback()
+	defer func() {
+		err = errors.Join(err, tx.Rollback())
+	}()
	qtx := queries.WithTx(tx)
	r, err := qtx.GetRecord(ctx, id)
	if err != nil {
		return err
	}
	if err := qtx.UpdateRecord(ctx, tutorial.UpdateRecordParams{
		ID:      r.ID,
		Counter: r.Counter + 1,
	}); err != nil {
		return err
	}
	return tx.Commit()
}

tx.Rollback()のエラーをキャッチするため、名前付き戻り値を採用し、erros.Join()を使って、tx.Rollback()のエラーを返すようにしました。
しかしこのままでは、正常にコミットされても、tx.Rollback()が実行されてしまい、以下のようなエラーが発生します。

sql: transaction has already been committed or rolled back

DB操作が失敗した時にのみ、tx.Rollback()を実行する

func bumpCounter(ctx context.Context, db *sql.DB, queries *tutorial.Queries, id int32) (err error) {
	tx, err := db.Begin()
	if err != nil {
		return err
	}
-	defer func() {
-		err = errors.Join(err, tx.Rollback())
-	}()
	qtx := queries.WithTx(tx)
	r, err := qtx.GetRecord(ctx, id)
	if err != nil {
-		return err
+		return errors.Join(err, tx.Rollback())
	}
	if err := qtx.UpdateRecord(ctx, tutorial.UpdateRecordParams{
		ID:      r.ID,
		Counter: r.Counter + 1,
	}); err != nil {
-		return err
+		return errors.Join(err, tx.Rollback())
	}
	return tx.Commit()
}

上記のようにすることで、tx.Rollback()が実行され、ロールバック時のエラーもキャッチできます。

最後に

多くのデータベースシステムでは、既にコミットされたトランザクションに対して Rollback() を呼び出しても、エラーは発生せず、単に無視されます。

とのことなので、別に気にしなくても良いのかもしれません。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?