From 8e88d70eb3934c786a5d7712bc687df2c457bdbf Mon Sep 17 00:00:00 2001 From: HowJMay Date: Sat, 25 Jan 2020 15:36:19 +0800 Subject: [PATCH 1/3] fix: Fix typo Fix some typos in codebase --- query_test.go | 2 +- tx.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/query_test.go b/query_test.go index 5b0e85e2..2d17afdb 100644 --- a/query_test.go +++ b/query_test.go @@ -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() diff --git a/tx.go b/tx.go index 91ffab9a..65b1d862 100644 --- a/tx.go +++ b/tx.go @@ -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. From 5082e30c4c1e6ebefe695c276032d35dbb31f8b5 Mon Sep 17 00:00:00 2001 From: Patrick Ellul Date: Tue, 28 Jan 2020 16:36:34 +1100 Subject: [PATCH 2/3] Implement maximum connection idle time (MaxConnIdleTime) for pgxpool. If a connection has been idle for longer than maxConnIdleTime, the health check will destroy it. --- go.mod | 2 +- go.sum | 2 ++ pgxpool/pool.go | 23 +++++++++++++++++++++-- pgxpool/pool_test.go | 23 +++++++++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 040ba170..9b854033 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 7b10e54d..20c51f28 100644 --- a/go.sum +++ b/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 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= diff --git a/pgxpool/pool.go b/pgxpool/pool.go index dd5a48b1..22a09e67 100644 --- a/pgxpool/pool.go +++ b/pgxpool/pool.go @@ -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{}), } @@ -233,6 +239,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 +292,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() } } } diff --git a/pgxpool/pool_test.go b/pgxpool/pool_test.go index bc02b0bf..d8ac4f98 100644 --- a/pgxpool/pool_test.go +++ b/pgxpool/pool_test.go @@ -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() From 2d20ba7ba41817e40cef5f8b40484244bb5517f6 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Sat, 1 Feb 2020 09:45:20 -0600 Subject: [PATCH 3/3] Add doc for pool_max_conn_idle_time option --- pgxpool/pool.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pgxpool/pool.go b/pgxpool/pool.go index 22a09e67..524373b9 100644 --- a/pgxpool/pool.go +++ b/pgxpool/pool.go @@ -191,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.