Skip to content

Commit a9d62cc

Browse files
authored
fix: Fix nested transactions should function correctly (#2470)
* fix: Fix nested transactions should function correctly Inner tx should reuse outer tx
1 parent 0d2f0d7 commit a9d62cc

File tree

2 files changed

+55
-1
lines changed

2 files changed

+55
-1
lines changed

coderd/database/db.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,17 @@ type sqlQuerier struct {
4747

4848
// InTx performs database operations inside a transaction.
4949
func (q *sqlQuerier) InTx(function func(Store) error) error {
50-
if q.sdb == nil {
50+
if _, ok := q.db.(*sql.Tx); ok {
51+
// If the current inner "db" is already a transaction, we just reuse it.
52+
// We do not need to handle commit/rollback as the outer tx will handle
53+
// that.
54+
err := function(q)
55+
if err != nil {
56+
return xerrors.Errorf("execute transaction: %w", err)
57+
}
5158
return nil
5259
}
60+
5361
transaction, err := q.sdb.Begin()
5462
if err != nil {
5563
return xerrors.Errorf("begin transaction: %w", err)

coderd/database/db_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//go:build linux
2+
3+
package database_test
4+
5+
import (
6+
"context"
7+
"testing"
8+
9+
"github.com/google/uuid"
10+
"github.com/stretchr/testify/require"
11+
12+
"github.com/coder/coder/coderd/database"
13+
)
14+
15+
func TestNestedInTx(t *testing.T) {
16+
t.Parallel()
17+
18+
uid := uuid.New()
19+
sqlDB := testSQLDB(t)
20+
err := database.MigrateUp(sqlDB)
21+
require.NoError(t, err, "migrations")
22+
23+
db := database.New(sqlDB)
24+
err = db.InTx(func(outer database.Store) error {
25+
return outer.InTx(func(inner database.Store) error {
26+
//nolint:gocritic
27+
require.Equal(t, outer, inner, "should be same transaction")
28+
29+
_, err := inner.InsertUser(context.Background(), database.InsertUserParams{
30+
ID: uid,
31+
Email: "coder@coder.com",
32+
Username: "coder",
33+
HashedPassword: []byte{},
34+
CreatedAt: database.Now(),
35+
UpdatedAt: database.Now(),
36+
RBACRoles: []string{},
37+
})
38+
return err
39+
})
40+
})
41+
require.NoError(t, err, "outer tx: %w", err)
42+
43+
user, err := db.GetUserByID(context.Background(), uid)
44+
require.NoError(t, err, "user exists")
45+
require.Equal(t, uid, user.ID, "user id expected")
46+
}

0 commit comments

Comments
 (0)