How does database/sql connection pooling work? Which settings matter and what causes connection leaks?
Question 456HardGo 1.22 to 1.25
*sql.DB is not a connection — it is a concurrency-safe pool, created once with sql.Open (which doesn't even connect; call PingContext). Each query checks out a connection and returns it when done.
SetMaxOpenConns— default unlimited; under load you can exceed the DB'smax_connections. Set it.SetMaxIdleConns— default 2; too low causes connection churn. Keep it close to MaxOpen.SetConnMaxLifetime/SetConnMaxIdleTime— recycle connections before load balancers/firewalls kill them silently.
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(25)
db.SetConnMaxLifetime(30 * time.Minute)
db.SetConnMaxIdleTime(5 * time.Minute)
rows, err := db.QueryContext(ctx, "SELECT id, name FROM users WHERE active = $1", true)
if err != nil {
return err
}
defer rows.Close() // returns the conn to the pool
for rows.Next() {
var u User
if err := rows.Scan(&u.ID, &u.Name); err != nil {
return err
}
users = append(users, u)
}
return rows.Err() // errors during iteration surface here
Leak causes: not closing *sql.Rows (early return inside the loop), using Query for statements that return no rows (use Exec), unfinished transactions (always defer tx.Rollback(); it's a no-op after Commit). With MaxOpen set, leaks show up as requests hanging forever — monitor db.Stats().WaitCount and WaitDuration.
More on Standard Library, HTTP & Systems Design in Go
- Q454json.Unmarshal vs json.Decoder: when to use each, and how do you safely decode a request body?
- Q455What does this print? (nil vs empty slices and maps in JSON)
- Q457How do you handle transactions, NULLs and "no rows" correctly with database/sql?
- Q458What is the exact io.Reader contract? What's wrong with this read loop?
- Q459Compose io primitives: upload a file to HTTP while computing its SHA-256 and limiting size, without buffering it in memory.
- Q460What are the pitfalls of bufio.Scanner and bufio.Writer?