Connection Pooling and Concurrency at Scale
Connection pooling keeps a managed set of open database connections and lends them to concurrent work. It reduces repeated connection setup and limits how many requests can occupy the database at once.
itDatabases and data storage | OpenSkills.info
Course pathWalk it in order
Look it upDip in anytime
Go furtherLeaves this page
Don't Panic
Don't Panic — Connection Pooling and Concurrency at Scale
Opening a database connection is more work than it looks: a network handshake, usually an encryption negotiation, an authentication round trip, and some memory set aside on the database server. Do all of that once per request and you have spent most of your time getting ready to work rather than working. A connection pool keeps a small set of these connections open and lends them out. A request borrows one, runs a query or a transaction, hands it back, and the connection stays open for the next borrower.
That is the dull half. The interesting half is that a pool is really a bounded queue: it has a maximum size, and when every connection is lent out, the next request waits. If it waits too long, an acquisition timeout ends the wait with a clean error instead of an indefinite hang. So the pool quietly does two jobs at once. It avoids repeated setup cost, and it caps how much work reaches the database.
The part that surprises people is that the maximum is not a user count. It is a limit on concurrent database work, and you size it from the database outward: start with the server's connection limit, subtract what administrators, migrations, monitoring and replication already take, then divide the rest across every application process that can reach the database. Autoscaling makes this arithmetic sharp. A pool of twenty looks modest until fifty replicas each want their twenty, and now you have asked for a thousand connections the database was never going to grant.
The other surprise is where the pool can sit. Inside each application process is the usual choice, but then every replica owns a separate pool with a separate maximum. A shared proxy, such as PgBouncer or a managed database proxy, sits between many clients and the database and enforces one backend ceiling for the whole fleet. Proxies can also loosen the reuse boundary: transaction pooling returns the connection after each commit instead of holding it for a whole client session, which packs far more clients onto the same backends, as long as the code does not depend on session state like temporary tables or prepared statements.
When a pool is exhausted, resist the reflex to enlarge it. A growing queue is evidence, and it usually points at slow queries, long transactions, a leaked connection that was borrowed and never returned, or a database already at its limit. A bigger pool often just relocates the queue from the application into the database, where waiting work is harder to see and harder to shed.
What to read next: the Intro lays out the full request path, the pool's internal states, and its failure behavior. Slides is the quick conceptual map. The Cheatsheet holds the sizing formulas and a symptom-to-cause table for when something is genuinely on fire. Field Notes covers what teams get wrong in production, and the Quiz checks whether the vocabulary has stuck.
Where this skill leads
Relevant careers
See how this topic contributes to broader role-level skill maps.
Sources
- https://docs.oracle.com/cd/B10500_01/java.920/a96654/connpoca.htm
Supports
- Physical and logical connection pooling model
- Reuse of established JDBC connections
- Reference-path rationale and beginner quiz
- https://www.postgresql.org/docs/current/runtime-config-connection.html
Supports
- PostgreSQL max_connections and reserved connection slots
- Resource implications of increasing max_connections
- max_connections can only be set at server start, so raising it requires a restart
- Shared memory and lock tables are sized from max_connections at boot
- Fleet-budget quiz
- Field Notes mistake card on raising the database connection limit
- https://wiki.postgresql.org/wiki/Number_Of_Database_Connections
Supports
- Pooling fewer database connections for more concurrent users
- Pool-sizing diagnostic guidance
- https://www.postgresql.org/docs/current/monitoring-stats.html
Supports
- pg_stat_activity state column values, including active and idle in transaction
- idle in transaction means an open transaction with no query running
- Grouping pg_stat_activity by state to observe backend usage
- Practice reference and exercise database-side checks
- Field Notes signal card on watching idle in transaction
- https://www.postgresql.org/docs/current/pgbench.html
Supports
- pgbench client count, thread count, and duration flags
- The -S select-only built-in transaction script
- pgbench -i initialization and scale factor
- Reported tps and latency average output
- Practice reference and exercise load generation
- https://docs.sqlalchemy.org/en/20/core/pooling.html
Supports
- Queue-based application pooling
- Pool size, overflow, acquisition timeout, recycle, reset, and pre-ping controls
- Logical close and pool-return semantics
- A pool must not be shared across process forks; each process needs its own pool
- Field Notes difficulty card on per-process pools
- https://github.com/brettwooldridge/HikariCP
Supports
- Acquisition timeout, idle timeout, keepalive, maximum lifetime, validation, and metrics
- Maximum pool behavior and connection retirement
- https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing
Supports
- Small saturated pool model
- Database contention from excess concurrent connections
- Load-testing and mixed-workload sizing guidance
- Field Notes tradeoff card on the recommended small pool
- https://www.pgbouncer.org/config
Supports
- Session, transaction, and statement pool modes
- Client, backend, reserve, reset, and timeout controls
- Session-state limits in transaction mode
- default_pool_size and max_client_conn semantics
- max_prepared_statements tracks protocol-level named prepared statements in transaction and statement mode
- server_reset_query is not used in transaction pooling mode
- PgBouncer Landscape placement
- Practice reference configuration and Field Notes shift card
- https://www.pgbouncer.org/usage
Supports
- Operational pool, client, server, and wait statistics
- SHOW POOLS, SHOW STATS, and SHOW SERVERS admin console counters
- cl_active, cl_waiting, sv_active, and sv_idle columns
- Practice reference and exercise pool inspection
- https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.howitworks.html
Supports
- Proxy pooling, borrowing, transaction multiplexing, and pinning
- Amazon RDS Proxy Landscape placement
- https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy-connections.html
Supports
- Application-pool lifetime and idle-timeout alignment
- Pinned idle clients holding database connections
- Connection borrow timeout
- https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy-best-practices.workload-considerations.html
Supports
- Pool and proxy sizing alignment
- Session state reducing multiplexing efficiency
- Multi-layer timeout alignment
- https://github.com/sindresorhus/awesome
Supports
- Discovery route to the PostgreSQL awesome list
- https://github.com/sroeschus/awesome-postgresql
Supports
- Discovery of PgBouncer, Odyssey, pgagroal, and the connection-pooling ecosystem
- https://pg-odyssey.tech/features/pooling.html
Supports
- Odyssey client multiplexing and backend resource use
- Awesome Links rationale and Landscape placement
- https://pgagroal.github.io/doc/GETTING_STARTED.html
Supports
- pgagroal setup, connection reuse, and backend pool behavior
- Awesome Links rationale
- https://pgagroal.github.io/doc/CONFIGURATION.html
Supports
- pgagroal maximum, idle, validation, and per-user or per-database controls
- Landscape placement
- https://github.com/postgresml/pgcat
Supports
- PgCat pooling, sharding, load balancing, failover, and mirroring scope
- Awesome Links rationale and Landscape placement
- https://www.pgpool.net/docs/latest/en/html/
Supports
- Pgpool-II connection reuse, load balancing, and high-availability scope
- Landscape placement
- https://supabase.github.io/supavisor/connecting/overview/
Supports
- Supavisor connection model
- Awesome Links rationale and Landscape placement
- https://supabase.com/docs/guides/database/connecting-to-postgres
Supports
- Session and transaction pooler placement for persistent and temporary clients
- https://neon.com/docs/connect/connection-pooling
Supports
- Neon pooled endpoint and transaction-mode PgBouncer behavior
- Landscape placement
- https://docs.prisma.io/docs/accelerate
Supports
- Managed pooling for Prisma ORM and serverless or edge applications
- Landscape placement
- https://www.pgbouncer.org/changelog.html
Supports
- Timeline research decision and 2007 first public release
- PgBouncer 1.21, released October 2023, added named prepared statement support in transaction mode
- Field Notes shift card on prepared statements in transaction mode
- https://aws.amazon.com/blogs/aws/amazon-rds-proxy-now-generally-available/
Supports
- Timeline research decision and 2019 preview plus 2020 general availability
