Skip to content

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

Description

@MartinodF

Describe the bug
Long story short, we use BeforeConnect to implement IAM Authentication with AWS / Aurora, and I traced back some panics to a missing logger in the context passed to BeforeConnect. I would expect the hook to receive a useful context (for logging, tracing, fetching credentials, ...), but while that happens for Pool.Acquire, the background goroutine refilling the pool to satisfy the minimum size calls it with a bare context.Background().

To Reproduce
Steps to reproduce the behavior:

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"
)

type key int

const myKey key = 0

func main() {
	ctx := context.WithValue(context.Background(), myKey, "value")

	poolConfig, _ := pgxpool.ParseConfig("postgres://user:password@127.0.0.1:1/mydb")

	// Fail fast
	poolConfig.ConnConfig.ConnectTimeout = 200 * time.Millisecond

	// Prepare connections in the background
	poolConfig.MinConns = 1
	poolConfig.HealthCheckPeriod = 300 * time.Millisecond

	var calls int
	poolConfig.BeforeConnect = func(bcCtx context.Context, cc *pgx.ConnConfig) error {
		calls++
		if v := bcCtx.Value(myKey); v == nil {
			fmt.Printf("call %d: BeforeConnect ctx MISSING value\n", calls)
		} else {
			fmt.Printf("call %d: BeforeConnect ctx has value=%q\n", calls, v)
		}
		return nil
	}

	pool, err := pgxpool.NewWithConfig(ctx, poolConfig)
	if err != nil {
		panic(err)
	}
	defer pool.Close()

	// Let the background healthcheck fire a few times.
	time.Sleep(2 * time.Second)
}

Expected behavior

call 1: ctx has value="value"
call 2: ctx has value="value"
call 3: ctx has value="value"

Actual behavior

call 1: ctx has value="value"
call 2: ctx MISSING value
call 3: ctx MISSING value

Call 1 comes from the initial idle creation goroutine in NewWithConfig. The subsequent ones come from backgroundHealthCheck + checkMinConns.

Impact
Anyone relying on context.Value propagation in BeforeConnect (e.g. injecting a request-scoped logger or AWS credential cache) sees inconsistent errors: the hook works on Acquire, it works on initial pool warm-up, but it randomly breaks on background refills. In our case it caused a nil-pointer panic while trying to log a message about the authentication.

Either re-use the context passed to NewWithConfig, expose a new Config.HealthCheckContext, or at the very least document the behavior to avoid unpleasant surprises. I can have a go at the first option if there's agreement.

Version

  • Go: go1.26.2 darwin/arm64
  • pgx: v5.9.1
  • puddle: v2.2.2

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions