Skip to content

fix(pgxpool): pass base context to BeforeConnect from background healthcheck#2546

Open
blackwell-systems wants to merge 2 commits into
jackc:masterfrom
blackwell-systems:fix-healthcheck-context
Open

fix(pgxpool): pass base context to BeforeConnect from background healthcheck#2546
blackwell-systems wants to merge 2 commits into
jackc:masterfrom
blackwell-systems:fix-healthcheck-context

Conversation

@blackwell-systems
Copy link
Copy Markdown

Description

checkMinConns passes context.Background() to createIdleResources, which means the Constructor function (and therefore BeforeConnect) receives a bare context with no values. Any hook that depends on context values from the original NewWithConfig call (loggers, AWS IAM credential caches, tracing spans) breaks silently on background refills while working correctly on Acquire and initial pool warm-up.

Fix

Store the context passed to NewWithConfig on the Pool struct (baseCtx) and use it in checkMinConns instead of context.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 in NewWithConfig, and replacing context.Background() with p.baseCtx in checkMinConns.

Impact

Any pgxpool user with MinConns > 0 or MinIdleConns > 0 who uses BeforeConnect with 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

…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>
Comment thread pgxpool/pool.go Outdated
healthCheckPeriod: config.HealthCheckPeriod,
healthCheckChan: make(chan struct{}, 1),
closeChan: make(chan struct{}),
baseCtx: ctx,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Suggested change
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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BeforeConnect receives context.Background() (instead of the pool context) from the healthcheck path

2 participants