Merge branch 'ellulpatrick-max-conn-idle-time'

pull/674/head
Jack Christensen 2020-02-01 09:45:37 -06:00
commit 06c3181836
6 changed files with 50 additions and 5 deletions

2
go.mod
View File

@ -9,7 +9,7 @@ require (
github.com/jackc/pgio v1.0.0
github.com/jackc/pgproto3/v2 v2.0.1
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-isatty v0.0.9 // indirect
github.com/rs/zerolog v1.15.0

2
go.sum
View File

@ -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 v1.0.0 h1:rbjAshlgKscNa7j0jAM0uNQflis5o2XUogPMVAwtcsM=
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/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=

View File

@ -14,6 +14,7 @@ import (
var defaultMaxConns = int32(4)
var defaultMaxConnLifetime = time.Hour
var defaultMaxConnIdleTime = time.Minute * 30
var defaultHealthCheckPeriod = time.Minute
type connResource struct {
@ -71,6 +72,7 @@ type Pool struct {
beforeAcquire func(context.Context, *pgx.Conn) bool
afterRelease func(*pgx.Conn) bool
maxConnLifetime time.Duration
maxConnIdleTime time.Duration
healthCheckPeriod time.Duration
closeChan chan struct{}
}
@ -92,9 +94,12 @@ type Config struct {
// return the connection to the pool or false to destroy the connection.
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
// 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 int32
@ -129,6 +134,7 @@ func ConnectConfig(ctx context.Context, config *Config) (*Pool, error) {
beforeAcquire: config.BeforeAcquire,
afterRelease: config.AfterRelease,
maxConnLifetime: config.MaxConnLifetime,
maxConnIdleTime: config.MaxConnIdleTime,
healthCheckPeriod: config.HealthCheckPeriod,
closeChan: make(chan struct{}),
}
@ -185,6 +191,7 @@ func ConnectConfig(ctx context.Context, config *Config) (*Pool, error) {
//
// pool_max_conns: integer greater than 0
// pool_max_conn_lifetime: duration string
// pool_max_conn_idle_time: duration string
// pool_health_check_period: duration string
//
// See Config for definitions of these arguments.
@ -233,6 +240,17 @@ func ParseConfig(connString string) (*Config, error) {
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 {
delete(connConfig.Config.RuntimeParams, "pool_health_check_period")
d, err := time.ParseDuration(s)
@ -275,8 +293,10 @@ func (p *Pool) checkIdleConnsHealth() {
for _, res := range resources {
if now.Sub(res.CreationTime()) > p.maxConnLifetime {
res.Destroy()
} else if res.IdleDuration() > p.maxConnIdleTime {
res.Destroy()
} else {
res.Release()
res.ReleaseUnused()
}
}
}

View File

@ -253,6 +253,29 @@ func TestPoolBackgroundChecksMaxConnLifetime(t *testing.T) {
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) {
t.Parallel()

View File

@ -185,7 +185,7 @@ func TestConnQueryValuesWhenUnableToDecode(t *testing.T) {
defer closeConn(t, conn)
// Note that this relies on pgtype.Record not supporting the text protocol. This seems safe as it is impossible to
// decode the text protocol because unlike the binary protocal there is no way to determine the OIDs of the elements.
// decode the text protocol because unlike the binary protocol there is no way to determine the OIDs of the elements.
rows, err := conn.Query(context.Background(), "select (array[1::oid], null)", pgx.QueryResultFormats{pgx.TextFormatCode})
require.NoError(t, err)
defer rows.Close()

2
tx.go
View File

@ -88,7 +88,7 @@ func (c *Conn) BeginTx(ctx context.Context, txOptions TxOptions) (Tx, error) {
// Tx represents a database transaction.
//
// Tx is an interface instead of a struct to enable connection pools to be implemented without relying on internal pgx
// state, to support psuedo-nested transactions with savepoints, and to allow tests to mock transactions. However,
// state, to support pseudo-nested transactions with savepoints, and to allow tests to mock transactions. However,
// adding a method to an interface is technically a breaking change. If new methods are added to Conn it may be
// desirable to add them to Tx as well. Because of this the Tx interface is partially excluded from semantic version
// requirements. Methods will not be removed or changed, but new methods may be added.