Skip to content

Commit 91c9e84

Browse files
jameshartigjackc
authored andcommitted
Ignore cancellation in puddle constructor
Fixes #1259
1 parent 88079de commit 91c9e84

File tree

2 files changed

+45
-0
lines changed

2 files changed

+45
-0
lines changed

pgxpool/pool.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ func (cr *connResource) getPoolRows(c *Conn, r pgx.Rows) *poolRows {
7070
return pr
7171
}
7272

73+
// detachedCtx wraps a context and will never be canceled, regardless of if
74+
// the wrapped one is cancelled. The Err() method will never return any errors.
75+
type detachedCtx struct {
76+
context.Context
77+
}
78+
79+
func (detachedCtx) Done() <-chan struct{} { return nil }
80+
func (detachedCtx) Deadline() (time.Time, bool) { return time.Time{}, false }
81+
func (detachedCtx) Err() error { return nil }
82+
7383
// Pool allows for connection reuse.
7484
type Pool struct {
7585
p *puddle.Pool
@@ -195,6 +205,14 @@ func ConnectConfig(ctx context.Context, config *Config) (*Pool, error) {
195205

196206
p.p = puddle.NewPool(
197207
func(ctx context.Context) (interface{}, error) {
208+
// we ignore cancellation on the original context because its either from
209+
// the health check or its from a query and we don't want to cancel creating
210+
// a connection just because the original query was cancelled since that
211+
// could end up stampeding the server
212+
// this will keep any Values in the original context and will just ignore
213+
// cancellation
214+
// see https://github.com/jackc/pgx/issues/1259
215+
ctx = detachedCtx{ctx}
198216
connConfig := p.config.ConnConfig
199217

200218
if p.beforeConnect != nil {

pgxpool/pool_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,33 @@ func TestLazyConnect(t *testing.T) {
7373
assert.Equal(t, context.Canceled, err)
7474
}
7575

76+
func TestConstructorIgnoresContext(t *testing.T) {
77+
t.Parallel()
78+
79+
config, err := pgxpool.ParseConfig(os.Getenv("PGX_TEST_DATABASE"))
80+
assert.NoError(t, err)
81+
config.LazyConnect = true
82+
var cancel func()
83+
config.BeforeConnect = func(context.Context, *pgx.ConnConfig) error {
84+
// cancel the query's context before we actually Dial to ensure the Dial's
85+
// context isn't cancelled
86+
cancel()
87+
return nil
88+
}
89+
90+
pool, err := pgxpool.ConnectConfig(context.Background(), config)
91+
require.NoError(t, err)
92+
93+
assert.EqualValues(t, 0, pool.Stat().TotalConns())
94+
95+
var ctx context.Context
96+
ctx, cancel = context.WithCancel(context.Background())
97+
defer cancel()
98+
_, err = pool.Exec(ctx, "SELECT 1")
99+
assert.ErrorIs(t, err, context.Canceled)
100+
assert.EqualValues(t, 1, pool.Stat().TotalConns())
101+
}
102+
76103
func TestConnectConfigRequiresConnConfigFromParseConfig(t *testing.T) {
77104
t.Parallel()
78105

0 commit comments

Comments
 (0)