From cd404aadbb5080795d3160d01eac7436d6087d35 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 17 Jun 2022 12:03:03 -0500 Subject: [PATCH 1/3] fix: Fix nested transactions should function correctly Inner tx should reuse outer tx --- coderd/database/db.go | 10 +++++++++- coderd/database/db_test.go | 41 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 coderd/database/db_test.go diff --git a/coderd/database/db.go b/coderd/database/db.go index 5236b18d65c82..0a9e8928df253 100644 --- a/coderd/database/db.go +++ b/coderd/database/db.go @@ -47,9 +47,17 @@ type sqlQuerier struct { // InTx performs database operations inside a transaction. func (q *sqlQuerier) InTx(function func(Store) error) error { - if q.sdb == nil { + if _, ok := q.db.(*sql.Tx); ok { + // If the current inner "db" is already a transaction, we just reuse it. + // We do not need to handle commit/rollback as the outer tx will handle + // that. + err := function(q) + if err != nil { + return xerrors.Errorf("execute transaction: %w", err) + } return nil } + transaction, err := q.sdb.Begin() if err != nil { return xerrors.Errorf("begin transaction: %w", err) diff --git a/coderd/database/db_test.go b/coderd/database/db_test.go new file mode 100644 index 0000000000000..768bdef353280 --- /dev/null +++ b/coderd/database/db_test.go @@ -0,0 +1,41 @@ +package database_test + +import ( + "context" + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + + "github.com/coder/coder/coderd/database" +) + +func TestNestedInTx(t *testing.T) { + uid := uuid.New() + sqlDB := testSQLDB(t) + err := database.MigrateUp(sqlDB) + require.NoError(t, err, "migrations") + + db := database.New(sqlDB) + err = db.InTx(func(outer database.Store) error { + return outer.InTx(func(inner database.Store) error { + require.Equal(t, outer, inner, "should be same transaction") + + _, err := inner.InsertUser(context.Background(), database.InsertUserParams{ + ID: uid, + Email: "coder@coder.com", + Username: "coder", + HashedPassword: []byte{}, + CreatedAt: database.Now(), + UpdatedAt: database.Now(), + RBACRoles: []string{}, + }) + return err + }) + }) + require.NoError(t, err, "outer tx: %w", err) + + user, err := db.GetUserByID(context.Background(), uid) + require.NoError(t, err, "user exists") + require.Equal(t, uid, user.ID, "user id expected") +} From 4ef788b9750ed7fc2f3d42908ebc3e2e97924574 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 17 Jun 2022 12:10:09 -0500 Subject: [PATCH 2/3] Linting to allow test --- coderd/database/db_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/coderd/database/db_test.go b/coderd/database/db_test.go index 768bdef353280..a0735979e873d 100644 --- a/coderd/database/db_test.go +++ b/coderd/database/db_test.go @@ -11,6 +11,8 @@ import ( ) func TestNestedInTx(t *testing.T) { + t.Parallel() + uid := uuid.New() sqlDB := testSQLDB(t) err := database.MigrateUp(sqlDB) @@ -19,6 +21,7 @@ func TestNestedInTx(t *testing.T) { db := database.New(sqlDB) err = db.InTx(func(outer database.Store) error { return outer.InTx(func(inner database.Store) error { + //nolint:gocritic require.Equal(t, outer, inner, "should be same transaction") _, err := inner.InsertUser(context.Background(), database.InsertUserParams{ From 8a29cf7383c18a03adb214c35191fe27480d2abb Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 17 Jun 2022 13:34:49 -0500 Subject: [PATCH 3/3] Only run test on linux --- coderd/database/db_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coderd/database/db_test.go b/coderd/database/db_test.go index a0735979e873d..bb8288d8ed38e 100644 --- a/coderd/database/db_test.go +++ b/coderd/database/db_test.go @@ -1,3 +1,5 @@ +//go:build linux + package database_test import (