feat: restore worker recycle limit via Caddyfile max_requests (default 500)
Tests / test (pull_request) Successful in 1m4s
Tests / test (pull_request) Successful in 1m4s
The removed runtime/frankenphp-symfony package force-restarted the
worker after FRANKENPHP_LOOP_MAX requests (default 500) - memory-leak
paranoia the built-in 8.1 runner intentionally doesn't reimplement.
Restore the behavior natively so the upgrade is a no-op operationally:
- Caddyfile: global frankenphp block with max_requests {$MAX_REQUESTS}
- per-thread graceful restarts, other threads keep serving
- Dockerfile: ARG/ENV MAX_REQUESTS=500 bakes the same default the old
package had, overridable at docker build or runtime (-e, 0 disables)
- Stock FRANKENPHP_CONFIG env var remains the full-config escape hatch
- Documented in readme env table, docs/example.env, CHANGELOG, and the
upgrade plan (phase-4 staging note now checks thread recycling)
This commit is contained in:
+14
-3
@@ -37,9 +37,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
8.1 handles FrankenPHP worker mode natively via its built-in
|
||||
`FrankenPhpWorkerRunner`. The `extra.runtime` override in
|
||||
`composer.json` was removed so the runtime auto-detects FrankenPHP.
|
||||
Note: the old package's `FRANKENPHP_LOOP_MAX` env var (default 500
|
||||
requests per worker) is no longer read; worker lifecycle is now managed
|
||||
by FrankenPHP itself.
|
||||
The old package's `FRANKENPHP_LOOP_MAX` env var is no longer read;
|
||||
an equivalent recycle limit is restored via the new `MAX_REQUESTS`
|
||||
setting below.
|
||||
|
||||
### Added
|
||||
- **`MAX_REQUESTS` worker-thread recycle limit** — The `Caddyfile` now
|
||||
sets FrankenPHP's native `max_requests` from the `MAX_REQUESTS`
|
||||
environment variable: each PHP worker thread is gracefully restarted
|
||||
after N requests while others keep serving, containing slow memory
|
||||
growth across long uptime. The image default is **500** (matching the
|
||||
previous `runtime/frankenphp-symfony` default), baked in as a Docker
|
||||
build arg and overridable at runtime (`MAX_REQUESTS=0` disables
|
||||
restarts). Arbitrary `frankenphp`-block configuration is still
|
||||
possible via the stock `FRANKENPHP_CONFIG` env var.
|
||||
|
||||
## [1.0.0] — v1.0 Release
|
||||
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
{
|
||||
frankenphp {
|
||||
# Restart each PHP worker thread after this many requests, containing
|
||||
# slow memory growth across long uptime. Preserves the 7.4-era default
|
||||
# loop count of runtime/frankenphp-symfony (500) after the Symfony 8.1
|
||||
# upgrade. Set MAX_REQUESTS=0 to disable restarts. The Dockerfile bakes
|
||||
# in the default of 500 via build arg; override at runtime with:
|
||||
# docker run -e MAX_REQUESTS=5000 ...
|
||||
# For full control, the stock FRANKENPHP_CONFIG env var can inject any
|
||||
# directive under this block instead.
|
||||
max_requests {$MAX_REQUESTS}
|
||||
}
|
||||
}
|
||||
|
||||
http://
|
||||
root public/
|
||||
rewrite index.php
|
||||
|
||||
@@ -45,6 +45,15 @@ ENV APP_DEBUG=0
|
||||
ENV APP_ENV=prod
|
||||
ENV APP_SHARE_DIR=/data/preauth
|
||||
|
||||
# worker thread lifecycle: restart each PHP thread after N requests to
|
||||
# contain slow memory growth. Matches the previous default loop count of
|
||||
# runtime/frankenphp-symfony (removed in the Symfony 8.1 upgrade).
|
||||
# Expose as a build arg so images can bake in a different default;
|
||||
# MAX_REQUESTS=0 disables restarts. Runtime override: the same env var is
|
||||
# read by the Caddyfile placeholder.
|
||||
ARG MAX_REQUESTS=500
|
||||
ENV MAX_REQUESTS=$MAX_REQUESTS
|
||||
|
||||
# load application into final image
|
||||
WORKDIR /app
|
||||
COPY --from=build /data/preauth /data/preauth
|
||||
|
||||
@@ -24,6 +24,14 @@
|
||||
# once blocked, do we respond with "I'm a teapot", false to use "Too many requests"
|
||||
#TEAPOT=true # default enabled, boolean
|
||||
|
||||
# --- server / worker options ---
|
||||
|
||||
# (container/deployment only) restart each FrankenPHP worker thread after
|
||||
# this many requests, containing memory growth across long uptime;
|
||||
# matching the default from the old runtime/frankenphp-symfony package.
|
||||
# 0 disables restarts. consumed by the Caddyfile, not the PHP app.
|
||||
#MAX_REQUESTS=500 # default 500
|
||||
|
||||
# --- remote-user header ---
|
||||
# Controls the value sent in the Remote-User header on successful auth.
|
||||
# session: the session id (default, backward-compatible)
|
||||
|
||||
@@ -1,12 +1,29 @@
|
||||
# Upgrade Plan: Symfony 7.4 → 8.1
|
||||
|
||||
**Status:** Planning
|
||||
**Status:** ✅ Implemented on branch `feat/symfony-8.1-upgrade-plan`
|
||||
(Phases 0–3 & state audit complete; Phases 4–5 = staging + release)
|
||||
**Target:** Symfony `8.1.*` (all symfony components)
|
||||
**Current:** Symfony `7.4.*`
|
||||
**Was:** Symfony `7.4.*` → **resolved 8.1.2–8.1.6**
|
||||
**Prepared:** 2026-09-07
|
||||
|
||||
---
|
||||
|
||||
## Implementation results
|
||||
|
||||
| Phase | Result |
|
||||
|-------|--------|
|
||||
| 0 Deprecation sweep | ✅ Clean — suite runs with `failOnDeprecation=true`, zero hits on 7.4; the 8.x jump needed **no app code changes**. |
|
||||
| 1 Composer bump | ✅ `runtime/frankenphp-symfony` removed, `extra.runtime` deleted, all `symfony/*` at `8.1.*` (framework-bundle 8.1.6, twig-bundle 8.1.2); ride-alongs PHPUnit 13.3.2, Twig 3.28, otphp 11.5. Boots on **v8.1.6**. |
|
||||
| 2 Config refresh | ✅ `config/reference.php` is gitignored, auto-regenerated by Flex. Prod `cache:clear`+`cache:warmup`, `lint:container`/`lint:yaml`/`lint:twig` all pass. |
|
||||
| 3 Tests | ✅ **295 tests / 612 assertions green** on 8.1; php-cs-fixer 0 fixable files. |
|
||||
| State audit | ✅ All `src/` services are `final readonly` with ctor-injected deps — no mutable state, kernel reuse under `FrankenPhpWorkerRunner` is safe. |
|
||||
| Loop-max parity | ✅ `Caddyfile` sets `max_requests {$MAX_REQUESTS}`; default **500** baked into the image via Docker build arg (matches old package default), runtime-overridable. See §2 note. |
|
||||
|
||||
Phases 4–5 (staging smoke + release) are pending — everything else in
|
||||
this document describes what was planned **and is now done**.
|
||||
|
||||
---
|
||||
|
||||
## 1. Why we can leapfrog 8.0
|
||||
|
||||
Symfony 7.4 and 8.0 were released simultaneously (Nov 2025) and are
|
||||
@@ -191,7 +208,7 @@ twig-bundle, uid, yaml) and tick each item against this codebase.
|
||||
| 1 | **composer bump**: branch `feat/symfony-8.1`; edit composer.json per §3–§4; `composer remove runtime/frankenphp-symfony`; `composer update`; re-sync recipes. | Installs clean on PHP 8.5; `bin/console about` shows 8.1.x. |
|
||||
| 2 | **Config refresh**: regenerate `config/reference.php`; review framework/twig/rate-limiter defaults; commit config changes. | `cache:clear` + warmup pass in dev & prod envs. |
|
||||
| 3 | **Tests**: full phpunit suite + php-cs-fixer; fix failures (expected: minor — event/type related). | Suite green in CI. |
|
||||
| 4 | **Staging smoke**: build image, run under FrankenPHP worker mode; verify TOTP login flow, backup codes, rate limiting (burst + teapot mode), public paths, central-auth subdomain flow; watch memory across >500 requests (old loop_max default no longer applies — see §2 note). | No state leaks across worker requests; healthcheck passes. |
|
||||
| 4 | **Staging smoke**: build image, run under FrankenPHP worker mode; verify TOTP login flow, backup codes, rate limiting (burst + teapot mode), public paths, central-auth subdomain flow; watch memory across >500 requests to confirm threads recycle via the Caddyfile `max_requests` setting (see §2 note). | No state leaks across worker requests; worker threads recycle at the configured request count; healthcheck passes. |
|
||||
| 5 | **Docs + release**: update readme/DESIGN_CONSIDERATIONS ("symfony 8.1, built-in FrankenPHP runtime"); tag a minor release per CHANGELOG conventions. | Release published; image rebuilt & pushed. |
|
||||
|
||||
**Rollback:** the upgrade is a single composer.lock + config diff.
|
||||
|
||||
@@ -113,6 +113,7 @@ for the complete reference.
|
||||
|----------|---------|-------------|
|
||||
| `IP_TTL` | `0` | Seconds to allow all traffic from an IP after login (0 = disabled). |
|
||||
| `TEAPOT` | `1` | Respond with 418 instead of 429 when rate-limited (boolean). |
|
||||
| `MAX_REQUESTS` | `500` | Restart each FrankenPHP worker thread after this many requests to contain memory growth (`0` = unlimited). Maps to the Caddyfile `max_requests` directive. |
|
||||
|
||||
### Remote-User Header
|
||||
|
||||
|
||||
Reference in New Issue
Block a user