Skip to content

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:

  1. First invocation in a fresh execution environment — Octane boots the Laravel application, opens the DB connection, performs the TLS handshake, authenticates. Handles the request.
  2. 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.
  3. At invocation BREF_LOOP_MAX — Octane terminates, Lambda recycles the execution context, next invocation starts fresh.
  4. Explicit per-request state reset — Octane runs Illuminate\Foundation\Http\Middleware\ResetContext between requests to clear request-scoped state. Database connections are explicitly preserved via OCTANE_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_MAX caps 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=1 and BREF_LOOP_MAX=250.
Last updated · 470 distilled / 1,213 read