OOD & LLD Reference/Classic LLD: Infrastructure Components

Connection Pool

Object pool for DB/HTTP connections — acquire/release, max size, idle eviction, health checks, and thread-safe blocking when exhausted.

4/5Overview: 35m

Problem framing

Design a connection pool for expensive resources (database connections, HTTP clients, gRPC channels): callers acquire() a ready connection, use it, then release() back to the pool instead of opening a new TCP+TLS session per request. Tests object lifecycle, bounded concurrency, and thread-safe blocking — the LLD counterpart to "why is HikariCP fast?"

ClassResponsibility
ConnectionPoolacquire(timeout), release(conn), owns idle + in-use sets
PoolConfigmaxSize, minIdle, maxIdleTimeMs, acquireTimeoutMs
PooledConnectionWrapper: raw connection + lastUsedAt, valid flag
ConnectionFactorycreate(), validate(conn), destroy(conn)
PoolMetrics (optional)active, idle, wait count — observability hook

Lifecycle

acquire(): if idle queue not empty → pop, validate, return (or destroy if stale) else if in-use count < maxSize → factory.create(), return else block until release() or timeout release(conn): if invalid or idle time exceeded → destroy else reset state (rollback txn, clear session) → push to idle queue notify one blocked acquirer

Factory isolates driver-specific code (JDBC URL, credentials) from pool logic — same pattern as Executor + ThreadFactory.

Thread-safety

ApproachTrade-off
BlockingQueue<PooledConnection>Simple acquire/release; size enforced by counters
Semaphore(maxSize) + setLimits in-use; idle stored separately
Per-connection mutexFine-grained; harder to reason about

Single lock on pool metadata is acceptable in interviews if you mention contention cost. Double-release is a bug — mark connection inUse or use wrapper tokens.

Health and eviction

Validate on checkoutSELECT 1 or socket ping; discard dead connections. Idle eviction — background thread removes connections idle > maxIdleTimeMs (HikariCP "housekeeping"). Max lifetime — rotate connections after N minutes regardless of idle (credential rotation, load balancer stickiness).

Senior-level signal

Contrast with per-request new connection: TLS handshake + auth latency dominates at high QPS. State pool sizing: connections ≈ ((core_count * 2) + effective_spindle_count) is a JDBC rule of thumb — cite as awareness, not gospel.

Distributed pool (PgBouncer, RDS Proxy) is out of scope — "this class is the in-process pool; proxy is another tier." Link to Concurrency roadmap: acquire blocking resembles semaphore + condition variable.

Common pitfalls

Returning a connection after caller forgot to rollback transaction — pool must reset() on release. Leak when caller never releases — mention try/finally or wrapper that auto-releases (scope guard / try-with-resources).

Where this goes next

Skip List / Ordered Set moves from lifecycle management to a class that has to stay correct under concurrent structural mutation — a different flavor of the thread-safety problem this topic keeps returning to.

Further Reading

Practice Tasks (Optional)

Design or implement locally in any language — no autograding. Focus on class structure, extensibility, and being able to explain trade-offs out loud.

  • Design ConnectionPool API

    Classes: ConnectionPool, PoolConfig, PooledConnection, ConnectionFactory. API: acquire(timeout), release(conn). Walk through: pool size 3, 4 concurrent acquirers — one blocks until release. Note health-check on checkout and max idle TTL.

    45m