Skip to content

Commit 1bd272d

Browse files
Support configurable ShouldPing func when acquiring conns
1 parent a11da9a commit 1bd272d

2 files changed

Lines changed: 63 additions & 1 deletion

File tree

pgxpool/pool.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ type Pool struct {
8787
prepareConn func(context.Context, *pgx.Conn) (bool, error)
8888
afterRelease func(*pgx.Conn) bool
8989
beforeClose func(*pgx.Conn)
90+
shouldPing func(context.Context, ShouldPingParams) bool
9091
minConns int32
9192
minIdleConns int32
9293
maxConns int32
@@ -104,6 +105,12 @@ type Pool struct {
104105
closeChan chan struct{}
105106
}
106107

108+
// ShouldPingParams are the parameters passed to ShouldPing.
109+
type ShouldPingParams struct {
110+
Conn *pgx.Conn
111+
IdleDuration time.Duration
112+
}
113+
107114
// Config is the configuration struct for creating a pool. It must be created by [ParseConfig] and then it can be
108115
// modified.
109116
type Config struct {
@@ -143,6 +150,10 @@ type Config struct {
143150
// BeforeClose is called right before a connection is closed and removed from the pool.
144151
BeforeClose func(*pgx.Conn)
145152

153+
// ShouldPing is called after a connection is acquired from the pool. If it returns true, the connection is pinged to check for liveness.
154+
// If this func is not set, the default behavior is to ping connections that have been idle for at least 1 second.
155+
ShouldPing func(context.Context, ShouldPingParams) bool
156+
146157
// MaxConnLifetime is the duration since creation after which a connection will be automatically closed.
147158
MaxConnLifetime time.Duration
148159

@@ -238,6 +249,14 @@ func NewWithConfig(ctx context.Context, config *Config) (*Pool, error) {
238249
p.releaseTracer = t
239250
}
240251

252+
if config.ShouldPing != nil {
253+
p.shouldPing = config.ShouldPing
254+
} else {
255+
p.shouldPing = func(ctx context.Context, params ShouldPingParams) bool {
256+
return params.IdleDuration > time.Second
257+
}
258+
}
259+
241260
var err error
242261
p.p, err = puddle.NewPool(
243262
&puddle.Config[*connResource]{
@@ -578,7 +597,8 @@ func (p *Pool) Acquire(ctx context.Context) (c *Conn, err error) {
578597

579598
cr := res.Value()
580599

581-
if res.IdleDuration() > time.Second {
600+
shouldPingParams := ShouldPingParams{Conn: cr.conn, IdleDuration: res.IdleDuration()}
601+
if p.shouldPing(ctx, shouldPingParams) {
582602
err := cr.conn.Ping(ctx)
583603
if err != nil {
584604
res.Destroy()

pgxpool/pool_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,48 @@ func TestPoolAcquireChecksIdleConns(t *testing.T) {
204204
require.NotContains(t, pids, cPID)
205205
}
206206

207+
func TestPoolAcquireChecksIdleConnsWithShouldPing(t *testing.T) {
208+
t.Parallel()
209+
210+
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
211+
defer cancel()
212+
213+
controllerConn, err := pgx.Connect(ctx, os.Getenv("PGX_TEST_DATABASE"))
214+
require.NoError(t, err)
215+
defer controllerConn.Close(ctx)
216+
pgxtest.SkipCockroachDB(t, controllerConn, "Server does not support pg_terminate_backend() (https://github.com/cockroachdb/cockroach/issues/35897)")
217+
218+
config, err := pgxpool.ParseConfig(os.Getenv("PGX_TEST_DATABASE"))
219+
require.NoError(t, err)
220+
221+
// Replace the default ShouldPing func
222+
var shouldPingLastCalledWith *pgxpool.ShouldPingParams
223+
config.ShouldPing = func(ctx context.Context, params pgxpool.ShouldPingParams) bool {
224+
shouldPingLastCalledWith = &params
225+
return false
226+
}
227+
228+
pool, err := pgxpool.NewWithConfig(ctx, config)
229+
require.NoError(t, err)
230+
defer pool.Close()
231+
232+
c, err := pool.Acquire(ctx)
233+
require.NoError(t, err)
234+
c.Release()
235+
236+
time.Sleep(time.Millisecond * 200)
237+
238+
c, err = pool.Acquire(ctx)
239+
require.NoError(t, err)
240+
conn := c.Conn()
241+
242+
require.NotNil(t, shouldPingLastCalledWith)
243+
assert.Equal(t, conn, shouldPingLastCalledWith.Conn)
244+
assert.InDelta(t, time.Millisecond*200, shouldPingLastCalledWith.IdleDuration, float64(time.Millisecond*100))
245+
246+
c.Release()
247+
}
248+
207249
func TestPoolAcquireFunc(t *testing.T) {
208250
t.Parallel()
209251

0 commit comments

Comments
 (0)