fix(pgxpool): pass base context to BeforeConnect from background healthcheck#2546
fix(pgxpool): pass base context to BeforeConnect from background healthcheck#2546blackwell-systems wants to merge 2 commits into
Conversation
…thcheck checkMinConns creates new connections with context.Background(), which means BeforeConnect hooks receive a bare context with no values. This breaks any hook that depends on context values from the original NewWithConfig call (loggers, AWS IAM credential caches, tracing spans). Store the context passed to NewWithConfig on the Pool struct and use it in checkMinConns instead of context.Background(). This matches the behavior of the initial idle resource creation at startup (line 337), which already uses the constructor context. Fixes jackc#2545 Signed-off-by: Dayna Blackwell <dayna@blackwell-systems.com>
| healthCheckPeriod: config.HealthCheckPeriod, | ||
| healthCheckChan: make(chan struct{}, 1), | ||
| closeChan: make(chan struct{}), | ||
| baseCtx: ctx, |
There was a problem hiding this comment.
Storing a context for reuse doesn't feel right. One concern is if it could be cancelled or carries a deadline, as all future refills would break. Removing those prior to storing would be safer, IMO:
| baseCtx: ctx, | |
| baseCtx: context.WithoutCancel(ctx), |
https://pkg.go.dev/context#WithoutCancel
That said, I wonder if the underlying issue is an incorrect use of context, ie., request-scoped vs. application-scoped. BeforeConnect seems like the right place for per-connection context enrichment, since it runs on every connection creation, whether for warmup or refill of the pool.
There was a problem hiding this comment.
Good catch, updated to context.WithoutCancel(ctx). That prevents a request-scoped context from breaking future refills while still preserving the values that BeforeConnect callbacks depend on (e.g., AWS IAM auth tokens).
On the context scope point: agreed, BeforeConnect is the right place for per-connection enrichment. The tricky part is that background healthchecks pass context.Background(), which drops all values from the original context. So users who store auth credentials in the context passed to NewWithConfig see it work on first connect but silently break on pool refill. WithoutCancel preserves the values without carrying over cancellation or deadlines.
There was a problem hiding this comment.
So users who store auth credentials in the context passed to NewWithConfig see it work on first connect but silently break on pool refill.
Credentials passed through the context seems like anti-pattern. I'd argue it points to the wrong injection point entirely. If credentials are fetched in BeforeConnect, they're available on every connection attempt regardless of what triggered it.
There was a problem hiding this comment.
Fair point. Fetching credentials directly in BeforeConnect is cleaner and avoids the context-propagation question entirely. That said, the current behavior (silently dropping to context.Background() on refill) is surprising regardless of how credentials are injected. Happy to adjust the approach if jackc has a preference on the design direction.
Description
checkMinConnspassescontext.Background()tocreateIdleResources, which means theConstructorfunction (and thereforeBeforeConnect) receives a bare context with no values. Any hook that depends on context values from the originalNewWithConfigcall (loggers, AWS IAM credential caches, tracing spans) breaks silently on background refills while working correctly onAcquireand initial pool warm-up.Fix
Store the context passed to
NewWithConfigon thePoolstruct (baseCtx) and use it incheckMinConnsinstead ofcontext.Background(). This matches the behavior of the initial idle resource creation at line 337, which already uses the constructor context.The change is 3 lines: one new field on
Pool, one assignment inNewWithConfig, and replacingcontext.Background()withp.baseCtxincheckMinConns.Impact
Any pgxpool user with
MinConns > 0orMinIdleConns > 0who usesBeforeConnectwith context values is affected. The most common case is AWS Aurora IAM authentication, where the credential refresh logic needs a logger from the context.Fixes #2545