Skip to content

Commit

Permalink
Ignore cancellation in puddle constructor
Browse files Browse the repository at this point in the history
Fixes #1259
  • Loading branch information
jameshartig authored and jackc committed Jul 30, 2022
1 parent 88079de commit 91c9e84
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 0 deletions.
18 changes: 18 additions & 0 deletions pgxpool/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ func (cr *connResource) getPoolRows(c *Conn, r pgx.Rows) *poolRows {
return pr
}

// detachedCtx wraps a context and will never be canceled, regardless of if
// the wrapped one is cancelled. The Err() method will never return any errors.
type detachedCtx struct {
context.Context
}

func (detachedCtx) Done() <-chan struct{} { return nil }
func (detachedCtx) Deadline() (time.Time, bool) { return time.Time{}, false }
func (detachedCtx) Err() error { return nil }

// Pool allows for connection reuse.
type Pool struct {
p *puddle.Pool
Expand Down Expand Up @@ -195,6 +205,14 @@ func ConnectConfig(ctx context.Context, config *Config) (*Pool, error) {

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

if p.beforeConnect != nil {
Expand Down
27 changes: 27 additions & 0 deletions pgxpool/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,33 @@ func TestLazyConnect(t *testing.T) {
assert.Equal(t, context.Canceled, err)
}

func TestConstructorIgnoresContext(t *testing.T) {
t.Parallel()

config, err := pgxpool.ParseConfig(os.Getenv("PGX_TEST_DATABASE"))
assert.NoError(t, err)
config.LazyConnect = true
var cancel func()
config.BeforeConnect = func(context.Context, *pgx.ConnConfig) error {
// cancel the query's context before we actually Dial to ensure the Dial's
// context isn't cancelled
cancel()
return nil
}

pool, err := pgxpool.ConnectConfig(context.Background(), config)
require.NoError(t, err)

assert.EqualValues(t, 0, pool.Stat().TotalConns())

var ctx context.Context
ctx, cancel = context.WithCancel(context.Background())
defer cancel()
_, err = pool.Exec(ctx, "SELECT 1")
assert.ErrorIs(t, err, context.Canceled)
assert.EqualValues(t, 1, pool.Stat().TotalConns())
}

func TestConnectConfigRequiresConnConfigFromParseConfig(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit 91c9e84

Please sign in to comment.