Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions pgxpool/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ type Pool struct {
maxConnLifetimeJitter time.Duration
maxConnIdleTime time.Duration
healthCheckPeriod time.Duration
acquireTimeout time.Duration

healthCheckChan chan struct{}

Expand Down Expand Up @@ -170,6 +171,9 @@ type Config struct {
// HealthCheckPeriod is the duration between checks of the health of idle connections.
HealthCheckPeriod time.Duration

// AcquireTimeout is the timeout for acquiring a connection from the pool
AcquireTimeout time.Duration

createdByParseConfig bool // Used to enforce created by ParseConfig rule.
}

Expand Down Expand Up @@ -225,6 +229,7 @@ func NewWithConfig(ctx context.Context, config *Config) (*Pool, error) {
maxConnLifetimeJitter: config.MaxConnLifetimeJitter,
maxConnIdleTime: config.MaxConnIdleTime,
healthCheckPeriod: config.HealthCheckPeriod,
acquireTimeout: config.AcquireTimeout,
healthCheckChan: make(chan struct{}, 1),
closeChan: make(chan struct{}),
}
Expand Down Expand Up @@ -566,6 +571,12 @@ func (p *Pool) Acquire(ctx context.Context) (c *Conn, err error) {
}()
}

if p.acquireTimeout > 0 {
var cancel func()
ctx, cancel = context.WithTimeout(ctx, p.acquireTimeout)
defer cancel()
}

// Try to acquire from the connection pool up to maxConns + 1 times, so that
// any that fatal errors would empty the pool and still at least try 1 fresh
// connection.
Expand Down
40 changes: 40 additions & 0 deletions pgxpool/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,46 @@ func TestPoolBackgroundChecksMinConns(t *testing.T) {
require.EqualValues(t, 3, stats.NewConnsCount())
}

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

ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
defer cancel()

config, err := pgxpool.ParseConfig(os.Getenv("PGX_TEST_DATABASE"))
require.NoError(t, err)

config.AcquireTimeout = 1 * time.Second

db, err := pgxpool.NewWithConfig(ctx, config)
require.NoError(t, err)

// Acquire all available connections to starve the pool
maxConns := int(db.Stat().MaxConns())
conns := make([]*pgxpool.Conn, 0, maxConns)
for i := 0; i < maxConns; i++ {
conn, err := db.Acquire(ctx)
require.NoError(t, err)

conns = append(conns, conn)
}

// If the timeout is not triggered within two seconds (2x the configured timeout), fail the test
timer := time.AfterFunc(2*time.Second, func() {
t.Fatal("Acquire() attempt did not timeout")
})

_, err = db.Acquire(ctx)
require.ErrorAs(t, err, &context.DeadlineExceeded)

timer.Stop()

for _, conn := range conns {
conn.Release()
}
waitForReleaseToComplete()
}

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

Expand Down
Loading