Connection Pooling & Performance
Read this page in the documentation
Connection Pooling & Performance Prorm manages database connections through a configurable pool so that repeated queries reuse warm connections instead of paying the cost of a fresh handshake every time. This guide covers how to size and tune the pool, retry transient failures, apply connection and query timeouts, watch pool health through events and metrics, and squeeze more performance out of individual queries. Every option shown here maps directly to the PoolOptions, RetryOptions, and QueryOptions types exposed by the library. Enabling the pool Pass a pool object to the Prorm constructor. Once configured, Prorm creates a DatabaseConnectionPool, initializes it, and exposes it as prorm.pool. If you omit pool, prorm.pool is null and connections are handled directly by the dialect driver. The lifecycle of one query The path to watch is queue → timeout. Those errors say the pool is exhausted, not that the database is slow — and they look identical from the outside unless you check whether the database is actually busy. Pool configuration The pool key accepts PoolOptions. All fields are optional and fall back to the defaults below. What min/max/idle/acquire actually do max caps concurrency. When every connection is busy, further requests queue. min keeps a floor of warm, idle connections so the first requests after a lull don't pay connection setup cost. The evictor never drops the pool below min. idle is how long a connection may sit unused before the background evictor closes it (respecting min). acquire is the ceiling on how long a caller waits in the queue for a free connection. If it expires, the acquire rejects with an "Acquisition timeout" error — a signal that max is too low or queries are holding connections too long. evict controls how frequently the evictor timer runs its idle sweep. Sizing the pool Pick max based on your database server's connection limit and your app's concurrency, not arbitrarily high — every open connection consumes server resources. Connection retry Transient failures — a database restart, a momentary network blip, "too many connections" — are worth retrying. Configure retry at the instance level with RetryOptions. When match is omitted, the dialect falls back to a built-in list of common transient connection errors (for example ECONNREFUSED, ENOTFOUND, ETIMEDOUT, connection timeout, too many connections). With backoff: true, the wait between attempts is timeout backoffMultiplier^attempt, capped at backoffMax. Per-query retry You can also opt a single query into retry logic (or override the instance defaults) by passing retry in the query options. Timeouts There are three timeouts worth tuning, at three different layers. connectTimeout governs the initial TCP/auth handshake. idleTimeout is the driver-level idle lifetime for a physical connection. pool.acquire is the wait-for-availability timeout described above. Individual queries can carry their own timeout as well: Inspecting the pool Prorm exposes the live pool and a lightweight stats snapshot. The pool object surfaces the same numbers as properties, plus configured limits: A pending count that stays above zero means requests are queuing for connections — a strong hint to raise max or to find the queries that hold connections too long. Pool events When enableEvents is on (the default), Prorm forwards pool lifecycle events on its own emitter under the pool: prefix. Use them for monitoring, tracing, or alerting. Repeated pool:enqueue and pool:timeout events are the clearest signal that the pool is saturated. Logging and slow-query detection Prorm emits a query event after every statement and a slowQuery event whenever a query exceeds slowQueryThreshold. Combined with the pool metrics, these are your main performance hooks. You can override logging per query — for example to silence a noisy statement or to route it to a custom sink: Per-query performance tips Reuse the pool, don't rebuild it. Construct one Prorm instance per process. Creating a new instance per request defeats pooling entirely. Keep connections checked out briefly. The pool serializes access once max is hit; long-running work while holding a connection starves everyone else. Do heavy in-memory processing after releasing. Use raw when you don't need mapping. Skipping model hydration avoids per-row object construction: Set a slowQueryThreshold low enough to catch regressions, then let the slowQuery event surface the offenders so you can add indexes or narrow selections. Match pool.max to real concurrency. An oversized pool can overwhelm the database server; an undersized one queues requests. Watch getPoolStats().pending and the pool:enqueue event to find the right value. Graceful shutdown Destroy the pool on shutdown so in-flight connections are closed cleanly and waiting requests are rejected rather than left hanging. destroy() with no argument tears down the whole pool; passing a single connection destroys just that one. Advanced: the standalone pool The pool that Prorm builds internally is also usable on its own via createPool. This is useful when you manage raw driver connections yourself but still want pooling, health checks, and validation. It accepts everything in PoolOptions plus factory, healthCheck, validateOnBorrow, and reconnection settings. Always release connections in a finally block. A connection that is never released stays "in use" forever, and once every connection leaks the pool exhausts and subsequent acquire() calls time out. Related reading Replication — splitting reads and writes across hosts SQL constants — why session state and pools interact badly