micro/go-micro · error
connection pool exhausted
Error message
connection pool exhausted
What it means
ErrPoolExhausted is the sentinel returned by the NATS connection pool when a caller requests a connection and all pooled connections are checked out (the internal connections channel is empty). Pool size is fixed at creation, so concurrency above that limit triggers this error.
Source
Thrown at transport/nats/pool.go:13
package nats
import (
"errors"
"sync"
"time"
natsp "github.com/nats-io/nats.go"
)
var (
// ErrPoolExhausted is returned when no connections are available in the pool
ErrPoolExhausted = errors.New("connection pool exhausted")
// ErrPoolClosed is returned when trying to use a closed pool
ErrPoolClosed = errors.New("connection pool is closed")
)
// connectionPool manages a pool of NATS connections
type connectionPool struct {
mu sync.RWMutex
connections chan *pooledConnection
factory func() (*natsp.Conn, error)
size int
idleTimeout time.Duration
closed bool
}
// pooledConnection wraps a NATS connection with metadata
type pooledConnection struct {
conn *natsp.Conn
createdAt time.TimeView on GitHub (pinned to 24529f1404)
Solutions
- Increase the pool size at transport creation to cover peak concurrency
- Ensure every Get has a matching Put, including on error paths (defer put)
- Add retry/backoff around Get to ride out transient exhaustion
- Check for stuck/leaked connections holding the pool indefinitely
Example fix
// before
conn, _ := pool.Get(ctx) // fails under burst
// after
conn, err := pool.Get(ctx)
if errors.Is(err, nats.ErrPoolExhausted) {
time.Sleep(50 * time.Millisecond)
conn, err = pool.Get(ctx)
}
defer pool.Put(conn) Defensive patterns
Strategy: retry
Validate before calling
// limit concurrency to pool size before calling Get
sem := make(chan struct{}, poolSize)
sem <- struct{}{}
defer func() { <-sem }() Try / catch
conn, err := pool.Get(ctx)
if errors.Is(err, nats.ErrPoolExhausted) {
select {
case <-time.After(100 * time.Millisecond):
conn, err = pool.Get(ctx)
case <-ctx.Done():
return ctx.Err()
}
} Prevention
- Always defer pool.Put(conn) immediately after a successful Get
- Size the pool for peak concurrent requests plus headroom
- Add metrics/alerts on pool in-use count
- Use bounded concurrency (semaphores) around pooled calls
When it happens
Trigger: Calling pool.Get() (or Dial when pooling enabled) while every connection in the fixed-size pool is currently borrowed and not yet returned via Put.
Common situations: Burst of concurrent requests exceeding pool size; leaked connections from code paths that Get without Put (error paths, panics); pool sized too small for service concurrency; slow operations holding connections long.
Related errors
- invalid connection from pool
- ErrPoolExhausted
- ErrPoolClosed
- invalid connection from pool
- connection pool is closed
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/f05fc391f255c33e.
Report an issue: GitHub.