PATTERN Cited by 1 source
Persistent process for serverless PHP DB connections¶
Intent¶
On AWS Lambda (or analogous serverless
compute), deploy PHP code behind a persistent-process request
handler (Laravel Octane / Swoole / RoadRunner / FrankenPHP —
under Bref, the
Bref\LaravelBridge\Http\OctaneHandler class) so that each
Lambda execution environment keeps the PHP process + DB connection
warm across up to N invocations, where N is bounded by
BREF_LOOP_MAX. This exits PHP's
shares-nothing request
model for the duration of one Lambda execution context's life,
recovering most of the per-request TLS handshake + Laravel
bootstrap cost without giving up Lambda's autoscale / pay-per-
request economics.
Context¶
- PHP-based application (Laravel / Symfony / custom) deployed to AWS Lambda via Bref.
- Database backend is TLS-terminated (PlanetScale, RDS with SSL, Aurora with SSL, any cloud managed MySQL / Postgres).
- Request-processing model is latency-sensitive enough that the per-request TLS handshake + framework bootstrap cost is unacceptable — typically >10 ms of request latency is attributable to connection setup rather than actual query execution.
Forces¶
- Shares-nothing PHP pays full setup cost per request. Every Lambda invocation in classic mode re-opens a TLS connection, re- authenticates against the database, re-bootstraps the Laravel service container. For a 0.3 ms database query this puts p50 at ~75 ms. (Source: sources/2026-04-21-planetscale-serverless-laravel-applications-with-aws-lambda-and-planetscale.)
- Serverless autoscale + shares-nothing amplifies per-tenant connection load on the database. See concepts/connection-pool-exhaustion. The server-side fix is patterns/two-tier-connection-pooling; this pattern is the complementary client-side fix.
- Lambda execution environments persist across invocations even though logical Lambda invocations are "stateless" in the marketing sense. A single execution context can handle hundreds to thousands of invocations before Lambda recycles it. Exploiting this is what makes the pattern viable.
- Persistent process introduces state-leak risk. Mutable static state, open file handles, partially-completed async work — all survive to the next request unless the persistent-process handler explicitly resets them.
Solution¶
Configure the Lambda function with a persistent-process handler (example: Laravel Octane) via Bref:
web:
handler: Bref\LaravelBridge\Http\OctaneHandler
runtime: php-81
environment:
BREF_LOOP_MAX: 250
OCTANE_PERSIST_DATABASE_SESSIONS: 1
events:
- httpApi: '*'
What this does:
- First invocation in a fresh execution environment — Octane boots the Laravel application, opens the DB connection, performs the TLS handshake, authenticates. Handles the request.
- Invocations 2 through
BREF_LOOP_MAX— Octane receives the request, routes through the already-booted Laravel application, executes the query on the already-established DB connection. Handshake, auth, bootstrap are all skipped. - At invocation
BREF_LOOP_MAX— Octane terminates, Lambda recycles the execution context, next invocation starts fresh. - Explicit per-request state reset — Octane runs
Illuminate\Foundation\Http\Middleware\ResetContextbetween requests to clear request-scoped state. Database connections are explicitly preserved viaOCTANE_PERSIST_DATABASE_SESSIONS=1.
Consequences¶
Benefits¶
- ~5× p50 latency reduction against a TLS-terminated database. Canonical benchmarked result against PlanetScale: p50 drops from 75 ms to 14 ms, p95 drops from 130 ms to 35 ms. (Source: sources/2026-04-21-planetscale-serverless-laravel-applications-with-aws-lambda-and-planetscale.)
- Amortisation of 61 ms of per-request setup cost — the TLS handshake + MySQL auth + Laravel bootstrap cost gets paid once per Lambda execution context, across 250 invocations, rather than once per invocation.
- Reduced database connection churn. With shares-nothing, every invocation opens and tears down a connection. With Octane, each Lambda execution environment holds one stable connection for its lifetime. Takes pressure off the database's connection accept queue and reduces wasted DB-side TLS handshake CPU.
- Autoscale / pay-per-request model preserved. The Lambda function still scales from 0 to many concurrent invocations identically; Octane runs within each Lambda execution environment, not across them.
Liabilities¶
- State-leak risk from mutable global / static state. Application code that sets a static property in request 1 sees it in request 2 unless explicitly reset. Typical footgun for code written under classic shares-nothing assumptions.
- Memory leaks compound over 250 invocations. Any per-request
allocation that isn't released accumulates.
BREF_LOOP_MAXcaps the damage but doesn't prevent it. - Connection staleness. A DB connection held for 250
invocations may hit server-side idle timeouts (
wait_timeout,interactive_timeout) if the Lambda execution context sits idle. Requires explicit liveness probes or reconnection-on-error logic. - Debuggability regression. Classic PHP's "each request is a fresh slate" was trivially debuggable. Persistent-process PHP has request-interaction bugs that are hard to reproduce locally.
- Framework support required. Not every PHP framework is Octane-compatible. Laravel has first-class support; Symfony has partial support via RoadRunner; other frameworks may require significant refactoring.
When to use¶
- You're on Lambda via Bref and have latency-sensitive PHP code hitting a TLS-terminated database.
- Your framework supports persistent-process mode (Laravel, Symfony with RoadRunner, Phalcon).
- You've audited your application for shares-nothing-implicit assumptions (static properties, global state) or can commit to doing so.
- You're willing to trade debugging simplicity for latency.
When not to use¶
- The application is not written with Octane-style persistent- process in mind and a full audit isn't feasible.
- Request latency isn't a constraint — if p50 at 75 ms is fine, the 5× improvement isn't worth the state-leak risk.
- The workload isn't TLS-terminated or the per-request setup cost is already negligible (e.g., talking to DynamoDB over AWS SDK which maintains its own connection pooling).
Seen in¶
- sources/2026-04-21-planetscale-serverless-laravel-applications-with-aws-lambda-and-planetscale
— canonical benchmarked demonstration of the pattern on AWS
Lambda + Bref + Laravel Octane + PlanetScale. Matthieu Napoli
(Bref creator), 2023-05-03. Demonstrates 5.4× p50 improvement
(75 ms → 14 ms) and 3.7× p95 improvement (130 ms → 35 ms)
against PlanetScale by switching from classic shares-nothing
Bref to Octane-persistent Bref with
OCTANE_PERSIST_DATABASE_SESSIONS=1andBREF_LOOP_MAX=250.
Related¶
- systems/laravel-octane — canonical implementation
- systems/bref — Lambda PHP runtime with Octane support
- systems/aws-lambda — deploy target
- systems/laravel — framework
- concepts/shared-nothing-php-request-model — the model the pattern escapes
- concepts/ssl-handshake-as-per-request-tax — the primary cost amortised
- concepts/connection-pool-exhaustion — the related failure mode
- concepts/cold-start — cold-start still applies to the first invocation of each Lambda execution context
- patterns/two-tier-connection-pooling — server-side complement