By Sagar Shankaran, Founder of CallSphere
Configure and optimize database connection pooling for AI agent applications comparing PgBouncer, pgpool-II, and application-level pools with monitoring strategies and troubleshooting guides.
Key takeaways
AI agent applications have a unique database access pattern. A single agent invocation might open a database connection, fetch context, then wait several seconds for an LLM to respond before writing the result back. During that LLM wait, the database connection sits idle — but it still consumes a PostgreSQL backend process, memory, and a slot in max_connections.
With 50 concurrent agent sessions each holding connections during LLM calls, you quickly exhaust PostgreSQL's default 100 connections. Connection pooling solves this by multiplexing many application connections over a smaller number of database connections, returning idle connections to the pool during LLM wait times.
Most ORMs and database drivers include built-in connection pools. Configure them correctly as your first line of defense:
flowchart LR
INPUT(["User intent"])
PARSE["Parse plus<br/>classify"]
PLAN["Plan and tool<br/>selection"]
AGENT["Agent loop<br/>LLM plus tools"]
GUARD{"Guardrails<br/>and policy"}
EXEC["Execute and<br/>verify result"]
OBS[("Trace and metrics")]
OUT(["Outcome plus<br/>next action"])
INPUT --> PARSE --> PLAN --> AGENT --> GUARD
GUARD -->|Pass| EXEC --> OUT
GUARD -->|Fail| AGENT
AGENT --> OBS
style AGENT fill:#4f46e5,stroke:#4338ca,color:#fff
style GUARD fill:#f59e0b,stroke:#d97706,color:#1f2937
style OBS fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
style OUT fill:#059669,stroke:#047857,color:#fff
SQLAlchemy (Python):
from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine(
"postgresql+asyncpg://user:pass@localhost/agents",
pool_size=10, # Steady-state connections
max_overflow=5, # Extra connections under load
pool_timeout=30, # Seconds to wait for a connection
pool_recycle=3600, # Recycle connections after 1 hour
pool_pre_ping=True, # Verify connections before use
)
Prisma (TypeScript):
# In DATABASE_URL
postgresql://user:pass@localhost/agents?connection_limit=15&pool_timeout=30
asyncpg (Python, direct driver):
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
import asyncpg
pool = await asyncpg.create_pool(
"postgresql://user:pass@localhost/agents",
min_size=5,
max_size=20,
max_inactive_connection_lifetime=300, # Drop idle connections after 5 min
)
The key rule: set your pool size to match your actual concurrent database usage, not your concurrent user count. If each agent request uses the database for 100ms out of a 3-second total request time, you need far fewer database connections than active requests.
PgBouncer sits between your application and PostgreSQL, pooling connections across all application instances:
# pgbouncer.ini
[databases]
agents = host=127.0.0.1 port=5432 dbname=agents
[pgbouncer]
listen_port = 6432
listen_addr = 0.0.0.0
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
default_pool_size = 20
max_client_conn = 200
reserve_pool_size = 5
reserve_pool_timeout = 3
server_idle_timeout = 600
The critical setting is pool_mode. Three options:
Use transaction mode for agent applications. It provides the best connection reuse because connections return to the pool between transactions. Your application sees 200 client connections but PostgreSQL only handles 20 actual connections.
Deploy PgBouncer as a sidecar container alongside your agent application pods:
# In your deployment spec
containers:
- name: agent-api
image: agent-api:latest
env:
- name: DATABASE_URL
value: "postgresql://user:pass@localhost:6432/agents"
- name: pgbouncer
image: bitnami/pgbouncer:latest
ports:
- containerPort: 6432
env:
- name: PGBOUNCER_DATABASE
value: agents
- name: POSTGRESQL_HOST
value: postgres-service.database.svc.cluster.local
- name: PGBOUNCER_POOL_MODE
value: transaction
- name: PGBOUNCER_DEFAULT_POOL_SIZE
value: "15"
- name: PGBOUNCER_MAX_CLIENT_CONN
value: "100"
The application connects to localhost:6432 (PgBouncer), which proxies to the actual PostgreSQL service. Each pod gets its own PgBouncer instance with dedicated pool management.
Track pool health with these queries:
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
-- Current PostgreSQL connections by state
SELECT state, count(*)
FROM pg_stat_activity
WHERE datname = 'agents'
GROUP BY state;
-- PgBouncer pool stats (connect to PgBouncer admin console)
-- pgbouncer admin: SHOW POOLS;
-- pgbouncer admin: SHOW STATS;
In your application, expose pool metrics:
from fastapi import FastAPI
app = FastAPI()
@app.get("/health/db")
async def db_health():
pool = get_pool()
return {
"pool_size": pool.get_size(),
"pool_free": pool.get_idle_size(),
"pool_used": pool.get_size() - pool.get_idle_size(),
"min_size": pool.get_min_size(),
"max_size": pool.get_max_size(),
}
"too many connections for role" — your total pool size across all instances exceeds PostgreSQL's max_connections. Calculate: instances * pool_max_size < max_connections - reserved_connections.
Connection timeouts under load — increase max_overflow (SQLAlchemy) or reserve_pool_size (PgBouncer) to handle burst traffic. Also check if connections are leaked by code paths that do not properly return connections to the pool.
Idle connections consuming memory — set pool_recycle or server_idle_timeout to close connections that have been idle too long. PostgreSQL consumes approximately 10MB per connection in memory.
Use both. Application-level pooling manages connections within a single process. PgBouncer manages connections across all processes and application instances. A typical production setup uses an application pool of 10-15 connections per instance, with PgBouncer sitting in front of PostgreSQL to further multiplex across instances.
Yes. In transaction mode, PgBouncer may route consecutive statements to different PostgreSQL backends, which do not share prepared statement caches. Most ORMs handle this transparently by using simple query protocol. If you use explicit prepared statements, switch to session mode or disable server-side prepare in your driver (statement_cache_size=0 in asyncpg).
Start with the formula: pool_size = (concurrent_requests * db_time_per_request) / total_request_time. For an agent API handling 100 concurrent requests where each request uses the database for 200ms out of a 4-second total, you need 100 * 0.2 / 4 = 5 connections. Add a 2-3x safety margin, giving you 10-15 connections. Monitor and adjust from there.
#ConnectionPooling #PgBouncer #PostgreSQL #Performance #AIAgents #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
A founder's guide to the personal AI assistant market: best AI assistant apps, business-grade options, and how CallSphere's voice agent fits in.
A founder's guide to free AI agents, low-code AI agent builders, and how to know when you should pay for a real platform like CallSphere.
Graphiti is the open-source temporal knowledge graph for AI agents in 2026. Learn how bi-temporal memory beats vector RAG for voice agents and long-running LLMs.
Chatbot app vs ChatGPT in 2026: a founder's clear take on the difference, when to use which, and how a real AI chatbot app development works.
How we built a fault-tolerant HVAC emergency triage and tech-dispatch platform on Kubernetes — three-tier CQRS, 11 micro-agents on the OpenAI Agents SDK + LangGraph, NATS JetStream, DTMF/SMS/WebSocket acceptance, circuit breakers, and an evaluation pipeline that catches regressions before they wake a tech at 3 AM.
Head-to-head: OpenAI Frontier and Anthropic's managed agent stack — strengths, fit, and what each means for enterprise AI voice and chat deployment.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.