A spike causes a function to fan out to 5000 concurrent executions. The database's connection limit is exhausted and everything fails. What are your options?
Show the full answer Hide the answer
What is being tested
Recognition that serverless elasticity is an amplifier, and that a component which scales instantly in front of one that cannot is a load-generation machine pointed at your own database.
The mechanism
The function platform scales to meet demand. The database does not. Each concurrent execution opens a connection; 5,000 executions exhaust a limit that is typically in the hundreds. Every subsequent connection attempt fails, including from other, healthy parts of the system that share the database.
The function's greatest strength has become the failure: it removed the natural backpressure that a fixed-size service fleet would have provided. A fleet of ten instances with a pool of twenty connections each simply queues; the excess load waits. Functions do not queue, they multiply.
The options, roughly in order
1. A connection pooler between functions and the database. The single most effective fix. The pooler holds a bounded set of real connections and multiplexes thousands of client connections onto them. This is close to mandatory in any serverless-plus-relational-database topology, and its absence is the most common cause of this exact incident.
2. Cap the function's reserved concurrency. Set an explicit maximum. Excess invocations are throttled or queued rather than executed. This is a one-line configuration change and it reintroduces the backpressure that was lost.
3. Put a queue in front. The spike lands in a queue; a bounded number of workers drain it at a rate the database can absorb. This converts a load spike into a latency increase, which is almost always the better failure — and it makes the throughput ceiling explicit rather than emergent.
4. Reduce per-invocation database work. Batch, cache, or move a read to a replica. Often the function did not need the database at all, or needed it once rather than per item.
5. Reconsider the store. A database designed for high connection counts and per-request billing matches the serverless model better than a connection-oriented relational engine. This is a large change and belongs last, not first.
The general principle
In any chain, the least elastic component defines the system's capacity. Placing an infinitely-elastic component in front of a fixed one without a limiter guarantees that the elastic one will destroy the fixed one under load. The limiter can be a pool, a concurrency cap, a queue or a rate limit — but something must exist to represent the downstream's ceiling.
This is the same principle behind bulkheads and admission control, and it is worth recognising in that general form, because it recurs everywhere: autoscaling application tiers in front of a single database, a retry policy in front of a struggling dependency, a batch job in front of a third-party API.
What a strong answer adds
Noting that platform-level retries make it worse. Many function platforms retry failed invocations automatically, so the connection failures generate more invocations against an already-exhausted database. Any fix must account for the retry behaviour, and side effects must be idempotent because those retries are not optional.