Skip to content

Commit 70c9a14

Browse files
authored
Merge pull request #2279 from djahandarie/min-idle-conns
Add MinIdleConns
2 parents 70f7cad + 6603ddf commit 70c9a14

File tree

3 files changed

+29
-3
lines changed

3 files changed

+29
-3
lines changed

pgxpool/common_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ func assertConfigsEqual(t *testing.T, expected, actual *pgxpool.Config, testName
147147
assert.Equalf(t, expected.MaxConnIdleTime, actual.MaxConnIdleTime, "%s - MaxConnIdleTime", testName)
148148
assert.Equalf(t, expected.MaxConns, actual.MaxConns, "%s - MaxConns", testName)
149149
assert.Equalf(t, expected.MinConns, actual.MinConns, "%s - MinConns", testName)
150+
assert.Equalf(t, expected.MinIdleConns, actual.MinIdleConns, "%s - MinIdleConns", testName)
150151
assert.Equalf(t, expected.HealthCheckPeriod, actual.HealthCheckPeriod, "%s - HealthCheckPeriod", testName)
151152

152153
assertConnConfigsEqual(t, expected.ConnConfig, actual.ConnConfig, testName)

pgxpool/pool.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717

1818
var defaultMaxConns = int32(4)
1919
var defaultMinConns = int32(0)
20+
var defaultMinIdleConns = int32(0)
2021
var defaultMaxConnLifetime = time.Hour
2122
var defaultMaxConnIdleTime = time.Minute * 30
2223
var defaultHealthCheckPeriod = time.Minute
@@ -87,6 +88,7 @@ type Pool struct {
8788
afterRelease func(*pgx.Conn) bool
8889
beforeClose func(*pgx.Conn)
8990
minConns int32
91+
minIdleConns int32
9092
maxConns int32
9193
maxConnLifetime time.Duration
9294
maxConnLifetimeJitter time.Duration
@@ -144,6 +146,13 @@ type Config struct {
144146
// to create new connections.
145147
MinConns int32
146148

149+
// MinIdleConns is the minimum number of idle connections in the pool. You can increase this to ensure that
150+
// there are always idle connections available. This can help reduce tail latencies during request processing,
151+
// as you can avoid the latency of establishing a new connection while handling requests. It is superior
152+
// to MinConns for this purpose.
153+
// Similar to MinConns, the pool might temporarily dip below MinIdleConns after connection closes.
154+
MinIdleConns int32
155+
147156
// HealthCheckPeriod is the duration between checks of the health of idle connections.
148157
HealthCheckPeriod time.Duration
149158

@@ -189,6 +198,7 @@ func NewWithConfig(ctx context.Context, config *Config) (*Pool, error) {
189198
afterRelease: config.AfterRelease,
190199
beforeClose: config.BeforeClose,
191200
minConns: config.MinConns,
201+
minIdleConns: config.MinIdleConns,
192202
maxConns: config.MaxConns,
193203
maxConnLifetime: config.MaxConnLifetime,
194204
maxConnLifetimeJitter: config.MaxConnLifetimeJitter,
@@ -271,7 +281,8 @@ func NewWithConfig(ctx context.Context, config *Config) (*Pool, error) {
271281
}
272282

273283
go func() {
274-
p.createIdleResources(ctx, int(p.minConns))
284+
targetIdleResources := max(int(p.minConns), int(p.minIdleConns))
285+
p.createIdleResources(ctx, targetIdleResources)
275286
p.backgroundHealthCheck()
276287
}()
277288

@@ -334,6 +345,17 @@ func ParseConfig(connString string) (*Config, error) {
334345
config.MinConns = defaultMinConns
335346
}
336347

348+
if s, ok := config.ConnConfig.Config.RuntimeParams["pool_min_idle_conns"]; ok {
349+
delete(connConfig.Config.RuntimeParams, "pool_min_idle_conns")
350+
n, err := strconv.ParseInt(s, 10, 32)
351+
if err != nil {
352+
return nil, fmt.Errorf("cannot parse pool_min_idle_conns: %w", err)
353+
}
354+
config.MinIdleConns = int32(n)
355+
} else {
356+
config.MinIdleConns = defaultMinIdleConns
357+
}
358+
337359
if s, ok := config.ConnConfig.Config.RuntimeParams["pool_max_conn_lifetime"]; ok {
338360
delete(connConfig.Config.RuntimeParams, "pool_max_conn_lifetime")
339361
d, err := time.ParseDuration(s)
@@ -472,7 +494,9 @@ func (p *Pool) checkMinConns() error {
472494
// TotalConns can include ones that are being destroyed but we should have
473495
// sleep(500ms) around all of the destroys to help prevent that from throwing
474496
// off this check
475-
toCreate := p.minConns - p.Stat().TotalConns()
497+
498+
// Create the number of connections needed to get to both minConns and minIdleConns
499+
toCreate := max(p.minConns-p.Stat().TotalConns(), p.minIdleConns-p.Stat().IdleConns())
476500
if toCreate > 0 {
477501
return p.createIdleResources(context.Background(), int(toCreate))
478502
}

pgxpool/pool_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,11 @@ func TestConnectConfig(t *testing.T) {
4343
func TestParseConfigExtractsPoolArguments(t *testing.T) {
4444
t.Parallel()
4545

46-
config, err := pgxpool.ParseConfig("pool_max_conns=42 pool_min_conns=1")
46+
config, err := pgxpool.ParseConfig("pool_max_conns=42 pool_min_conns=1 pool_min_idle_conns=2")
4747
assert.NoError(t, err)
4848
assert.EqualValues(t, 42, config.MaxConns)
4949
assert.EqualValues(t, 1, config.MinConns)
50+
assert.EqualValues(t, 2, config.MinIdleConns)
5051
assert.NotContains(t, config.ConnConfig.Config.RuntimeParams, "pool_max_conns")
5152
assert.NotContains(t, config.ConnConfig.Config.RuntimeParams, "pool_min_conns")
5253
}

0 commit comments

Comments
 (0)