mirror of https://github.com/jackc/pgx.git
Implement maximum connection idle time (MaxConnIdleTime) for pgxpool.
If a connection has been idle for longer than maxConnIdleTime, the health check will destroy it.pull/670/head
parent
366c926137
commit
5082e30c4c
2
go.mod
2
go.mod
|
@ -9,7 +9,7 @@ require (
|
||||||
github.com/jackc/pgio v1.0.0
|
github.com/jackc/pgio v1.0.0
|
||||||
github.com/jackc/pgproto3/v2 v2.0.1
|
github.com/jackc/pgproto3/v2 v2.0.1
|
||||||
github.com/jackc/pgtype v1.1.0
|
github.com/jackc/pgtype v1.1.0
|
||||||
github.com/jackc/puddle v1.0.0
|
github.com/jackc/puddle v1.0.1-0.20200126004755-807afe48a83d
|
||||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.9 // indirect
|
github.com/mattn/go-isatty v0.0.9 // indirect
|
||||||
github.com/rs/zerolog v1.15.0
|
github.com/rs/zerolog v1.15.0
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -62,6 +62,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9 h1:KLBBPU++1T3DHtm1B1
|
||||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v1.0.0 h1:rbjAshlgKscNa7j0jAM0uNQflis5o2XUogPMVAwtcsM=
|
github.com/jackc/puddle v1.0.0 h1:rbjAshlgKscNa7j0jAM0uNQflis5o2XUogPMVAwtcsM=
|
||||||
github.com/jackc/puddle v1.0.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.0.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.0.1-0.20200126004755-807afe48a83d h1:lYLhmugF2D1ysJgU4pyW/GcdH+X4O3T96duzNdxcHqY=
|
||||||
|
github.com/jackc/puddle v1.0.1-0.20200126004755-807afe48a83d/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
|
|
||||||
var defaultMaxConns = int32(4)
|
var defaultMaxConns = int32(4)
|
||||||
var defaultMaxConnLifetime = time.Hour
|
var defaultMaxConnLifetime = time.Hour
|
||||||
|
var defaultMaxConnIdleTime = time.Minute * 30
|
||||||
var defaultHealthCheckPeriod = time.Minute
|
var defaultHealthCheckPeriod = time.Minute
|
||||||
|
|
||||||
type connResource struct {
|
type connResource struct {
|
||||||
|
@ -71,6 +72,7 @@ type Pool struct {
|
||||||
beforeAcquire func(context.Context, *pgx.Conn) bool
|
beforeAcquire func(context.Context, *pgx.Conn) bool
|
||||||
afterRelease func(*pgx.Conn) bool
|
afterRelease func(*pgx.Conn) bool
|
||||||
maxConnLifetime time.Duration
|
maxConnLifetime time.Duration
|
||||||
|
maxConnIdleTime time.Duration
|
||||||
healthCheckPeriod time.Duration
|
healthCheckPeriod time.Duration
|
||||||
closeChan chan struct{}
|
closeChan chan struct{}
|
||||||
}
|
}
|
||||||
|
@ -92,9 +94,12 @@ type Config struct {
|
||||||
// return the connection to the pool or false to destroy the connection.
|
// return the connection to the pool or false to destroy the connection.
|
||||||
AfterRelease func(*pgx.Conn) bool
|
AfterRelease func(*pgx.Conn) bool
|
||||||
|
|
||||||
// MaxConnLifetime is the duration after which a connection will be automatically closed.
|
// MaxConnLifetime is the duration since creation after which a connection will be automatically closed.
|
||||||
MaxConnLifetime time.Duration
|
MaxConnLifetime time.Duration
|
||||||
|
|
||||||
|
// MaxConnIdleTime is the duration after which an idle connection will be automatically closed by the health check.
|
||||||
|
MaxConnIdleTime time.Duration
|
||||||
|
|
||||||
// MaxConns is the maximum size of the pool.
|
// MaxConns is the maximum size of the pool.
|
||||||
MaxConns int32
|
MaxConns int32
|
||||||
|
|
||||||
|
@ -129,6 +134,7 @@ func ConnectConfig(ctx context.Context, config *Config) (*Pool, error) {
|
||||||
beforeAcquire: config.BeforeAcquire,
|
beforeAcquire: config.BeforeAcquire,
|
||||||
afterRelease: config.AfterRelease,
|
afterRelease: config.AfterRelease,
|
||||||
maxConnLifetime: config.MaxConnLifetime,
|
maxConnLifetime: config.MaxConnLifetime,
|
||||||
|
maxConnIdleTime: config.MaxConnIdleTime,
|
||||||
healthCheckPeriod: config.HealthCheckPeriod,
|
healthCheckPeriod: config.HealthCheckPeriod,
|
||||||
closeChan: make(chan struct{}),
|
closeChan: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
@ -233,6 +239,17 @@ func ParseConfig(connString string) (*Config, error) {
|
||||||
config.MaxConnLifetime = defaultMaxConnLifetime
|
config.MaxConnLifetime = defaultMaxConnLifetime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s, ok := config.ConnConfig.Config.RuntimeParams["pool_max_conn_idle_time"]; ok {
|
||||||
|
delete(connConfig.Config.RuntimeParams, "pool_max_conn_idle_time")
|
||||||
|
d, err := time.ParseDuration(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf("invalid pool_max_conn_idle_time: %w", err)
|
||||||
|
}
|
||||||
|
config.MaxConnIdleTime = d
|
||||||
|
} else {
|
||||||
|
config.MaxConnIdleTime = defaultMaxConnIdleTime
|
||||||
|
}
|
||||||
|
|
||||||
if s, ok := config.ConnConfig.Config.RuntimeParams["pool_health_check_period"]; ok {
|
if s, ok := config.ConnConfig.Config.RuntimeParams["pool_health_check_period"]; ok {
|
||||||
delete(connConfig.Config.RuntimeParams, "pool_health_check_period")
|
delete(connConfig.Config.RuntimeParams, "pool_health_check_period")
|
||||||
d, err := time.ParseDuration(s)
|
d, err := time.ParseDuration(s)
|
||||||
|
@ -275,8 +292,10 @@ func (p *Pool) checkIdleConnsHealth() {
|
||||||
for _, res := range resources {
|
for _, res := range resources {
|
||||||
if now.Sub(res.CreationTime()) > p.maxConnLifetime {
|
if now.Sub(res.CreationTime()) > p.maxConnLifetime {
|
||||||
res.Destroy()
|
res.Destroy()
|
||||||
|
} else if res.IdleDuration() > p.maxConnIdleTime {
|
||||||
|
res.Destroy()
|
||||||
} else {
|
} else {
|
||||||
res.Release()
|
res.ReleaseUnused()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,6 +253,29 @@ func TestPoolBackgroundChecksMaxConnLifetime(t *testing.T) {
|
||||||
assert.EqualValues(t, 0, stats.TotalConns())
|
assert.EqualValues(t, 0, stats.TotalConns())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPoolBackgroundChecksMaxConnIdleTime(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
config, err := pgxpool.ParseConfig(os.Getenv("PGX_TEST_DATABASE"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
config.MaxConnLifetime = 1 * time.Minute
|
||||||
|
config.MaxConnIdleTime = 100 * time.Millisecond
|
||||||
|
config.HealthCheckPeriod = 150 * time.Millisecond
|
||||||
|
|
||||||
|
db, err := pgxpool.ConnectConfig(context.Background(), config)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
c, err := db.Acquire(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
c.Release()
|
||||||
|
time.Sleep(config.HealthCheckPeriod + 50*time.Millisecond)
|
||||||
|
|
||||||
|
stats := db.Stat()
|
||||||
|
assert.EqualValues(t, 0, stats.TotalConns())
|
||||||
|
}
|
||||||
|
|
||||||
func TestPoolExec(t *testing.T) {
|
func TestPoolExec(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue