Skip to content

Commit 7aa1751

Browse files
authored
Fix bad bug in riversharedtest.DBPool (which is used by TestTx) (#846)
Fixes a bad bug in `riversharedtest.DBPool` in which while lazily initializing a database pool, *we never actually set the database pool*, so repeated invocations of `DBPool` would check for an existing pool that would never exist and create a new one each time. This project seems to be brute forcing its way by, but I was seeing Postgres connection exhaustions in other projects. Boiled down, the problem is that we had this: dbPool, err := pgxpool.New(ctx, cmp.Or( os.Getenv("TEST_DATABASE_URL"), "postgres://localhost:5432/river_test", )) When it should've been this: var err error dbPool, err = pgxpool.New(ctx, cmp.Or( os.Getenv("TEST_DATABASE_URL"), "postgres://localhost:5432/river_test", )) Here, fix the problem, and go a step further to simplify the code somewhat to use a `sync.Once` instead of mutex. The final product is fewer lines of code, which will hopefully make a bug of this magnitude easier to spot should it reoccur.
1 parent 83cb7a6 commit 7aa1751

2 files changed

Lines changed: 18 additions & 29 deletions

File tree

rivershared/riversharedtest/riversharedtest.go

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,43 +32,28 @@ func BaseServiceArchetype(tb testing.TB) *baseservice.Archetype {
3232
}
3333
}
3434

35-
// A pool and mutex to protect it, lazily initialized by TestTx. Once open, this
35+
// A pool and sync.Once to initialize it, invoked by TestTx. Once open, this
3636
// pool is never explicitly closed, instead closing implicitly as the package
3737
// tests finish.
3838
var (
39-
dbPool *pgxpool.Pool //nolint:gochecknoglobals
40-
dbPoolMu sync.RWMutex //nolint:gochecknoglobals
39+
dbPool *pgxpool.Pool //nolint:gochecknoglobals
40+
dbPoolOnce sync.Once //nolint:gochecknoglobals
4141
)
4242

4343
// DBPool gets a lazily initialized database pool for `TEST_DATABASE_URL` or
4444
// `river_test` if the former isn't specified.
4545
func DBPool(ctx context.Context, tb testing.TB) *pgxpool.Pool {
4646
tb.Helper()
4747

48-
tryPool := func() *pgxpool.Pool {
49-
dbPoolMu.RLock()
50-
defer dbPoolMu.RUnlock()
51-
return dbPool
52-
}
53-
54-
if dbPool := tryPool(); dbPool != nil {
55-
return dbPool
56-
}
57-
58-
dbPoolMu.Lock()
59-
defer dbPoolMu.Unlock()
60-
61-
// Multiple goroutines may have passed the initial `nil` check on start
62-
// up, so check once more to make sure pool hasn't been set yet.
63-
if dbPool != nil {
64-
return dbPool
65-
}
66-
67-
dbPool, err := pgxpool.New(ctx, cmp.Or(
68-
os.Getenv("TEST_DATABASE_URL"),
69-
"postgres://localhost:5432/river_test",
70-
))
71-
require.NoError(tb, err)
48+
dbPoolOnce.Do(func() {
49+
var err error
50+
dbPool, err = pgxpool.New(ctx, cmp.Or(
51+
os.Getenv("TEST_DATABASE_URL"),
52+
"postgres://localhost:5432/river_test",
53+
))
54+
require.NoError(tb, err)
55+
})
56+
require.NotNil(tb, dbPool) // die in case initial connect from another test failed
7257

7358
return dbPool
7459
}

rivershared/riversharedtest/riversharedtest_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,13 @@ func TestDBPool(t *testing.T) {
1515

1616
ctx := context.Background()
1717

18-
pool := DBPool(ctx, t)
19-
_, err := pool.Exec(ctx, "SELECT 1")
18+
pool1 := DBPool(ctx, t)
19+
_, err := pool1.Exec(ctx, "SELECT 1")
2020
require.NoError(t, err)
21+
22+
// Both pools should be exactly the same object.
23+
pool2 := DBPool(ctx, t)
24+
require.Equal(t, pool1, pool2)
2125
}
2226

2327
func TestTestTx(t *testing.T) {

0 commit comments

Comments
 (0)