@@ -17,6 +17,7 @@ import (
1717
1818var defaultMaxConns = int32 (4 )
1919var defaultMinConns = int32 (0 )
20+ var defaultMinIdleConns = int32 (0 )
2021var defaultMaxConnLifetime = time .Hour
2122var defaultMaxConnIdleTime = time .Minute * 30
2223var 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 }
0 commit comments