Commit Graph

15 Commits

Author SHA1 Message Date
Nicolas Grekas 5a9bc7fb14 feat: Add configurable max_requests for PHP threads (#2292)
PHP-FPM recycles worker processes after a configurable number of
requests (`pm.max_requests`), preventing memory leaks from accumulating
over time. FrankenPHP keeps PHP threads alive indefinitely, so any leak
in PHP extensions (e.g. ZTS builds of profiling tools like Blackfire) or
application code compounds over hours/days. In production behind reverse
proxies like Cloudflare, this can lead to gradual resource exhaustion
and eventually 502 errors with no visible warnings in logs.

This PR adds a `max_requests` option in the global `frankenphp` block
that automatically restarts PHP threads after a given number of
requests, fully cleaning up the thread's memory and state. It applies to
both regular (module mode) and worker threads.

When a thread reaches the limit it exits the C thread loop, triggering a
full cleanup including any memory leaked by extensions. A fresh thread
is then booted transparently. Other threads continue serving requests
during the restart.

This cannot be done from userland PHP: restarting a worker script from
PHP only resets PHP-level state, not the underlying C thread-local
storage where extension-level leaks accumulate. And in module mode
(without workers), there is no userland loop to count requests at all.

Default is `0` (unlimited), preserving existing behavior.

Usage:

```caddyfile
{
    frankenphp {
        max_requests 500
    }
}
```

Changes:
- New `max_requests` Caddyfile directive in the global `frankenphp`
block
- New `WithMaxRequests` functional option
- New `Rebooting` and `RebootReady` states in the thread state machine
for restart coordination
- Regular thread restart in `threadregular.go`
- Worker thread restart in `threadworker.go`
- Safe shutdown: `shutdown()` waits for in-flight reboots to complete
before draining threads
- Tests for both module and worker mode (sequential and concurrent),
with debug log verification
- Updated docs
2026-04-16 14:15:56 +02:00
Alexandre Daubois 5ebf119b2b feat: add more metrics to the Prometheus endpoint 2026-03-26 11:37:24 +01:00
Alexander Stecher 8f4412cbbf perf: move sandboxed environment to the C side (#2058)
This PR uses `zend_array_dup` to simplify and optimize the environment sandboxing
logic. It also guarantees no environment leakage on FrankenPHP restarts.
2026-02-26 22:34:54 +01:00
Alexander Stecher 98573ed7c0 refactor: extract the state module and make the backoff error instead of panic
This PR:
- moves state.go to its own module
- moves the phpheaders test the phpheaders module
- simplifies backoff.go
- makes the backoff error instead of panic (so it can be tested)
- removes some unused C structs
2025-12-02 23:10:12 +01:00
Alexander Stecher dadeb5a628 perf: tail latency with goSched (#2033)
Alternate implementation to #2016 that doesn't reduce RPS with lower
amounts of threads
2025-11-26 18:33:07 +01:00
Kévin Dunglas 8341cc98c6 refactor: rely on context.Context for log/slog and others (#1969)
* refactor: rely on context.Context for log/slog and others

* optimize

* refactor

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix watcher-skip

* better globals handling

* fix

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-17 16:32:23 +01:00
Kévin Dunglas 6225da9c18 refactor: improve ExtensionWorkers API (#1952)
* refactor: improve ExtensionWorkers API

* Update workerextension.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update workerextension.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update caddy/app.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* review

* fix tests

* docs

* errors

* improved error handling

* fix race

* add missing return

* use %q in Errorf

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-10 14:12:14 +01:00
Alexander Stecher c10e85b905 refactor: cleanup context (#1816)
* Removes NewRequestWithContext.

* Moves cgi logic to cgi.go

* Calls 'update_request_info' from the C side.

* Calls 'update_request_info' from the C side.

* clang-format

* Removes unnecessary export.

* Adds TODO.

* Adds TODO.

* Removes 'is_worker_thread'

* Shortens return statement.

* Removes the context refactor.

* adjusts comment.

* Skips parsing cgi path variables on explicitly assigned worker.

* suggesions by @dunglas.

* Re-introduces 'is_worker_thread'.

* More formatting.
2025-08-25 16:18:20 +02:00
Kévin Dunglas cf7541fde6 chore: add more logs for the worker 2025-05-19 22:43:54 +02:00
Alexander Stecher ab0fcd80de Fixes metrics also with regular request timeouts. (#1550) 2025-05-10 14:31:58 +02:00
Kévin Dunglas 8092f4a35c chore!: update to golangci-lint-action 7 (#1508) 2025-04-17 20:33:22 +02:00
Alexander Stecher 9cca12858b feat: maximum wait times (#1445) 2025-03-19 13:21:37 +01:00
Alexander Stecher f50248a7d2 refactor: removes context on the C side (#1404) 2025-03-10 08:44:03 +01:00
Alexander Stecher c57f741d83 fix: concurrent env access (#1409) 2025-03-01 14:45:04 +01:00
Alliballibaba2 072151dfee feat: Adds automatic thread scaling at runtime and php_ini configuration in Caddyfile (#1266)
Adds option to scale threads at runtime

Adds php_ini configuration in Caddyfile
2025-02-19 20:39:33 +01:00