# Why Serverless Functions Overwhelm Databases

> Understand database connection pressure, serverless fan-out, pooling, and the transaction and session constraints that limit reuse.

- Author: Aman Kumar Gupta (akgbytes)
- Published: 2026-06-14
- Tags: Serverless, Databases, Connection Pooling
- Series: Modern Web Systems from First Principles, part 5
- Canonical source: https://akgbytes.in/blogs/why-serverless-functions-overwhelm-databases

Serverless compute can create more execution environments as traffic rises. A traditional database cannot necessarily accept connections at the same rate.

That mismatch produces a common failure: the application tier scales successfully while the database runs out of connection capacity.

The fix begins with understanding what a database connection represents. It is not just a URL used for one query.

## How a database connection works

An application normally reaches PostgreSQL or MySQL through a long-lived network connection.

Creating that connection can involve DNS lookup, a TCP connection, TLS negotiation, protocol startup, and authentication. Only then can the client send queries.

```text
Application
  → network connection
  → TLS and authentication
  → database session
  → query and result messages
```

Opening this path for every query repeats setup work. Database drivers therefore keep connections open and reuse them.

The database must track each active connection and its session. Connection limits protect it from spending unbounded resources on clients instead of useful query work.

There is no reliable universal “memory per connection” number. Cost varies by database engine, version, configuration, session activity, and workload.

The important fact is structural: connections consume finite database resources even when the application tier can create clients elastically.

## Connections are stateful

A database connection carries a **session**, not just bytes.

Session state can include the authenticated role, current transaction, configuration values, temporary objects, locks, and prepared statements.

In PostgreSQL, a prepared statement belongs to its database session. Another session cannot simply execute it by name.

Transactions are even stricter. Every statement between `BEGIN` and `COMMIT` or `ROLLBACK` must observe one consistent transactional context.

```sql
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
```

Sending the second statement through an unrelated database session would break the transaction.

This state is why connection reuse has rules. A pool cannot hand a busy connection to arbitrary work and hope the results remain correct.

## Creating and holding connections has a cost

Connection setup consumes network round trips and authentication work. TLS adds cryptographic work. The database allocates resources to represent and serve the session.

An idle connection avoids repeating setup, but it still occupies a connection slot and may retain session resources.

Keeping some connections ready is useful. Keeping a large pool in every application process can turn idle capacity into a database outage.

The right capacity depends on query duration, request concurrency, database limits, and how much work the database can execute efficiently.

More connections do not automatically create more throughput. Past a useful concurrency level, they can add waiting and contention inside the database.

## Serverless fan-out during a traffic spike

Imagine one long-running API process with a pool of ten connections. The database sees at most that process's configured pool size.

Now imagine a platform creates 100 function environments and each environment is allowed a pool of ten.

The theoretical client-side total becomes 1,000 connections. The database sees the sum across environments, not the friendly limit configured in one process.

Not every environment will fill its pool, and platforms differ in concurrency and reuse. But multiplying a local pool by a changing instance count is the essential risk.

Cold environments can also arrive together and authenticate together. That connection storm adds setup pressure exactly when query traffic is already high.

![A traffic burst fanning out across function environments and overwhelming a database connection budget.](https://akgbytes.in/articles/diagrams/serverless-fan-out.svg)

Warm reuse helps but cannot be assumed. A function may reuse an existing client, disappear after becoming idle, or run beside many newly created environments.

Serverless scaling moves the bottleneck. It does not make the downstream database elastic by association.

## What connection pooling does

A connection pool maintains database connections and lends them to work that needs them.

```text
request → borrow connection → run work → release connection
```

If all suitable connections are busy, new work waits until one is returned or a timeout is reached. This applies backpressure instead of opening connections without bound.

A pool reduces repeated setup and caps the connections it controls. It does not reduce query cost or guarantee that the database can process every incoming request.

Pool settings are capacity controls:

- **Maximum size** limits open connections owned by that pool.
- **Acquisition timeout** limits how long work waits for a connection.
- **Idle timeout** removes unused connections.
- **Maximum lifetime** replaces connections periodically.

Configure these values with the database's total budget in mind, including application replicas, functions, background jobs, migrations, and administrator access.

## Application pools versus external poolers

An application pool lives inside one process or execution environment. It is simple and removes reconnect work while that environment remains alive.

In serverless systems, each environment can own a separate pool. Pools do not coordinate their maximums, and short-lived environments may provide little reuse.

Use one lazily created pool or client per environment when the driver and platform support it. Avoid constructing a new pool inside every request handler.

An external pooler or managed proxy sits between many application clients and the database.

```text
many application clients → pooler/proxy → fewer database connections
```

It can enforce a database-side connection budget across application environments. Some proxies also multiplex many client connections over fewer database connections.

The pooler is not infinite capacity. It needs limits, monitoring, availability planning, and timeouts. A full pool changes overload into waiting or rejection rather than creating database throughput.

![Independent function connection pools compared with a coordinated database connection budget.](https://akgbytes.in/articles/diagrams/db-pool.svg)

## Transactions and pooling limitations

Pooling modes decide when a database connection can return to the pool.

In **session pooling**, one client keeps the same database connection for its whole session. Session features behave naturally, but multiplexing is limited.

In **transaction pooling**, the client keeps one database connection for a transaction. After the transaction ends, the pooler may assign that connection elsewhere.

This enables more reuse, but code must not assume that session state survives between transactions.

Session-scoped prepared statements, `SET` values, advisory locks, temporary tables, and `LISTEN` can require special handling or make transaction pooling unsuitable.

Capabilities differ by pooler and version. PgBouncer, for example, documents which PostgreSQL features are compatible with each mode and has specific prepared-statement behavior.

A protocol-aware proxy may **pin** a client session to one database connection when it cannot safely multiplex the session state.

Pinning preserves correctness, but pinned connections cannot serve other clients. Heavy use of session state can erase much of the proxy's scaling benefit.

Keep transactions short, always release clients in a `finally` block, and avoid leaving transactions idle. An open transaction holds both the connection and database-side transactional resources.

## Practical connection management

Start with a budget, not a default pool size.

Reserve capacity for operations outside request handling. Divide the remaining connections across the maximum application environments you expect, or enforce the budget with an external proxy.

Then measure these signals together:

- Open, active, and idle database connections
- Pool wait time and acquisition timeouts
- Query latency and database CPU
- Transaction duration and idle transactions
- Proxy multiplexing or pinning metrics
- Function concurrency and cold-start rate

A high pool wait time with a healthy database may justify careful capacity changes. A saturated database means increasing pool size will usually feed the bottleneck faster.

Apply backpressure before failure. Bound queues and acquisition timeouts let the application reject excess work predictably instead of waiting forever.

Retry only transient failures, with a limit and jitter. Unbounded retries amplify a connection storm.

Keep compute and the database geographically close. Pooling cannot remove the network latency added to every query round trip.

Finally, test bursts rather than only steady traffic. A system that handles 1,000 requests over a minute may still fail when they arrive in one second.

## Key takeaways

- A database connection is a stateful, finite resource.
- Serverless environments can multiply local pool limits during a burst.
- Warm connection reuse helps performance but is not guaranteed.
- Pools reuse setup work and enforce a connection budget; they do not add query capacity.
- External poolers can coordinate connections across many environments.
- Transactions must stay on one database connection until they end.
- Session state can restrict multiplexing or pin a connection.
- Connection limits, timeouts, backpressure, and workload metrics must be designed together.

## What comes next

Pooling helps serverless compute use a traditional database safely. Some databases go further by changing how compute, storage, and connection handling scale.

The next article asks what the heavily overloaded term **serverless database** should actually mean.

## Further reading

- [PostgreSQL documentation: prepared statements](https://www.postgresql.org/docs/current/sql-prepare.html)
- [PgBouncer feature compatibility by pooling mode](https://www.pgbouncer.org/features.html)
- [AWS RDS Proxy concepts: pooling, multiplexing, and pinning](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.howitworks.html)