Connection Pool Configuration
A job queue system opens many concurrent database connections – one per worker
goroutine, plus connections for heartbeats, scheduling, and the stale lock
reaper. To keep that from overwhelming the database, NewGormStorage installs a
bounded default pool automatically (see below), so you get a safe pool without
writing any configuration. Pool tuning is an optional optimization on top of
that default – you reach for it to match a specific workload (higher
concurrency, latency-sensitive paths, or a tight cloud connection cap), not to
avoid an unbounded pool.
The simple-durable-jobs library exposes Go’s database/sql pool settings
through a clean option-based API so you can tune connection usage to match your
workload.
Default Pool (Applied Automatically)
NewGormStorage(db) installs a bounded connection pool unless the pool has
already been sized. You do not have to configure anything to be safe – the
unbounded-by-default behavior of database/sql is overridden for you.
| Driver | MaxOpen | MaxIdle | MaxLifetime | MaxIdleTime |
|---|---|---|---|---|
| PostgreSQL / MySQL | 25 | 10 | 5 min | 1 min |
| SQLite | 4 | 2 | none | none |
PostgreSQL and MySQL receive DefaultPoolConfig() values (25/10/5 min/1 min) –
the real guard against exhausting a Postgres max_connections limit. SQLite
receives a small, single-writer-friendly pool (4 open, 2 idle) with no
connection expiry: a zero lifetime/idle-time keeps the sole connection of an
in-memory (:memory:) database from being dropped (which would silently discard
the database) and keeps WAL connections warm.
The default is applied only when the pool is still unset. The constructor checks
the underlying *sql.DB and skips the default when:
- The caller already sized the pool before construction (any non-zero
MaxOpenConns, e.g. adb.DB().SetMaxOpenConns(...)orConfigurePoolcall made beforeNewGormStorage). - Storage is created via
NewGormStorageWithPool, which has already sized the pool itself – including an explicitMaxOpenConns(0)meaning unlimited. The auto-default is suppressed in this case so your unlimited choice is honored rather than silently re-bounded.
A nil DB, or a DB whose *sql.DB cannot be obtained (a mock or
dialector-only connection), is skipped silently so construction stays
panic-free.
Creating Storage with Pool Configuration
Using a Preset
The fastest way to get started is to pick a preset that matches your deployment profile:
storage, err := jobs.NewGormStorageWithPool(db, jobs.WithPoolConfig(jobs.HighConcurrencyPoolConfig()))
if err != nil {
log.Fatal(err)
}Customizing Individual Settings
You can mix and match individual pool options:
storage, err := jobs.NewGormStorageWithPool(db,
jobs.MaxOpenConns(50),
jobs.MaxIdleConns(20),
jobs.ConnMaxLifetime(10 * time.Minute),
jobs.ConnMaxIdleTime(2 * time.Minute),
)
if err != nil {
log.Fatal(err)
}Configuring an Existing Database
If you already have a *gorm.DB that you opened elsewhere, you can apply pool
settings to it directly and then create storage normally:
err := jobs.ConfigurePool(db, jobs.MaxOpenConns(100))
if err != nil {
log.Fatal(err)
}
storage := jobs.NewGormStorage(db)Available Presets
Each preset returns a PoolConfig struct with values tuned for a specific
deployment scenario:
| Preset | MaxOpen | MaxIdle | MaxLifetime | MaxIdleTime | Use Case |
|---|---|---|---|---|---|
DefaultPoolConfig() | 25 | 10 | 5 min | 1 min | General purpose |
HighConcurrencyPoolConfig() | 100 | 25 | 10 min | 2 min | 50+ workers, high throughput |
LowLatencyPoolConfig() | 50 | 40 | 15 min | 5 min | Latency-sensitive workloads |
ResourceConstrainedPoolConfig() | 10 | 5 | 3 min | 30 s | Limited DB resources |
DefaultPoolConfig
Suitable for most workloads. Allows up to 25 concurrent database connections with 10 kept warm in the idle pool. Connections are recycled every 5 minutes to prevent stale connections from accumulating.
HighConcurrencyPoolConfig
Designed for deployments running 50 or more worker goroutines. Opens up to 100 connections and keeps 25 idle. Longer lifetimes reduce the overhead of re-establishing connections under sustained load.
LowLatencyPoolConfig
Optimized for workloads where connection acquisition time matters. Keeps 40 of 50 connections warm in the idle pool, so most dequeue and completion operations hit a pre-established connection. Longer idle times prevent unnecessary teardown.
ResourceConstrainedPoolConfig
Use this when your database has strict connection limits (for example, a small managed database instance or a serverless database with a low connection cap). Limits the pool to 10 open and 5 idle connections with aggressive recycling.
Individual Options
Each option sets a single field on the underlying PoolConfig:
| Option | Description |
|---|---|
MaxOpenConns(n int) | Maximum number of open database connections. Set to 0 for unlimited (not recommended for production). |
MaxIdleConns(n int) | Maximum number of idle connections kept in the pool. Should be less than or equal to MaxOpenConns. |
ConnMaxLifetime(d time.Duration) | Maximum amount of time a connection can be reused before it is closed and replaced. Set to 0 for no limit (not recommended). |
ConnMaxIdleTime(d time.Duration) | Maximum amount of time a connection can sit idle before it is closed. Set to 0 for no limit. |
Choosing the Right Configuration
Here are guidelines to help you pick the right settings:
Match MaxOpenConns to Worker Concurrency
Every worker goroutine may hold a database connection while processing a job. Add connections for heartbeats, the stale lock reaper, the scheduler, and the dequeue poll loop. A safe starting point is:
MaxOpenConns >= total worker concurrency + 10For example, if you run three queues with concurrency 20, 10, and 5 (total 35),
set MaxOpenConns to at least 45.
Size the Idle Pool at 25-50% of MaxOpenConns
Idle connections avoid the latency of establishing a new TCP connection and TLS handshake. For typical workloads, keeping 25-50% of connections warm provides a good balance between responsiveness and resource usage.
Set ConnMaxLifetime to 5-15 Minutes
Recycling connections periodically prevents issues with stale TCP sessions, load-balancer idle timeouts, and DNS changes in cloud environments. Five minutes is a good default; increase to 10-15 minutes under sustained high throughput to reduce churn.
Respect Cloud Database Limits
Many managed database services impose connection limits (for example, a small
PostgreSQL instance may allow only 100 connections shared across all
applications). Use ResourceConstrainedPoolConfig or manually set
MaxOpenConns to stay within your quota. Remember that each application replica
has its own pool, so divide the limit across replicas.