Add configurable max_idle_time for autoscaled threads
The idle timeout for autoscaled threads is currently hardcoded to 5
seconds. With bursty traffic patterns, this causes threads to be
deactivated too quickly, leading to repeated cold-start overhead when
the next burst arrives.
This PR replaces the hardcoded constant with a configurable
max_idle_time directive, allowing users to tune how long idle
autoscaled threads stay alive before deactivation. The default remains 5
seconds, preserving existing behavior.
Usage:
Caddyfile:
````
frankenphp {
max_idle_time 30s
}
````
JSON config:
```
{
"frankenphp": {
"max_idle_time": "30s"
}
}
````
Changes:
- New max_idle_time Caddyfile directive and JSON config option
- New WithMaxIdleTime functional option
- Replaced hardcoded maxThreadIdleTime constant with configurable
maxIdleTime variable
- Added tests for custom and default idle time behavior
- Updated docs
Introduces C-side interned string registry (frankenphp_strings) and a frankenphp_server_vars struct to bulk-register known $_SERVER variables with pre-sized hashtable capacity.
As discussed in https://github.com/php/frankenphp/discussions/1961,
there is no real way to pass a severity/level to any log handler offered
by PHP that would make it to the FrankenPHP layer. This new function
allows applications embedding FrankenPHP to integrate PHP logging into
the application itself, thus offering a more streamlined experience.
---------
Co-authored-by: Quentin Burgess <qutn.burgess@gmail.com>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
This patch brings hot reloading capabilities to PHP apps: in
development, the browser will automatically refresh the page when any
source file changes!
It's similar to HMR in JavaScript.
It is built on top of [the watcher
mechanism](https://frankenphp.dev/docs/config/#watching-for-file-changes)
and of the [Mercure](https://frankenphp.dev/docs/mercure/) integration.
Each time a watched file is modified, a Mercure update is sent, giving
the ability to the client to reload the page, or part of the page
(assets, images...).
Here is an example implementation:
```caddyfile
root ./public
mercure {
subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY}
anonymous
}
php_server {
hot_reload
}
```
```php
<?php
header('Content-Type: text/html');
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
<script>
const es = new EventSource('<?=$_SERVER['FRANKENPHP_HOT_RELOAD']?>');
es.onmessage = () => location.reload();
</script>
</head>
<body>
Hello
```
I plan to create a helper JS library to handle more advanced cases
(reloading CSS, JS, etc), similar to [HotWire
Spark](https://github.com/hotwired/spark). Be sure to attend my
SymfonyCon to learn more!
There is still room for improvement:
- Provide an option to only trigger the update without reloading the
worker for some files (ex, images, JS, CSS...)
- Support classic mode (currently, only the worker mode is supported)
- Don't reload all workers when only the files used by one change
However, this PR is working as-is and can be merged as a first step.
This patch heavily refactors the watcher module. Maybe it will be
possible to extract it as a standalone library at some point (would be
useful to add a similar feature but not tight to PHP as a Caddy module).
---------
Signed-off-by: Kévin Dunglas <kevin@dunglas.fr>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* add "nosys" tag to not pull in system include locations
* rename to "nosysinc"
* Revert "rename to "nosysinc""
This reverts commit a7ff2a0fd9.
* remove paths all together
* bring back rpath for macos
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
---------
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
* add module (php_server directive) based workers
* refactor moduleID to uintptr for faster comparisons
* let workers inherit environment variables and root from php_server
* caddy can shift FrankenPHPModules in memory for some godforsaken reason, can't rely on them staying the same
* remove debugging statement
* fix tests
* refactor moduleID to uint64 for faster comparisons
* actually allow multiple workers per script filename
* remove logging
* utility function
* reuse existing worker with same filename and environment when calling newWorker with a filepath that already has a suitable worker, simply add number of threads
* no cleanup happens between tests, so restore old global worker overwriting logic
* add test, use getWorker(ForContext) function in frankenphp.go as well
* bring error on second global worker with the same filename again
* refactor to using name instead of moduleID
* nicer name
* nicer name
* add more tests
* remove test case already covered by previous test
* revert back to single variable, moduleIDs no longer relevant
* update comment
* figure out the worker to use in FrankenPHPModule::ServeHTTP
* add caddy/config_tests, add --retry 5 to download
* add caddy/config_tests
* sum up logic a bit, put worker thread addition into moduleWorkers parsing, before workers are actually created
* implement suggestions as far as possible
* fixup
* remove tags
* feat: download the mostly static binary when possible (#1467)
* feat: download the mostly static binary when possible
* cs
* docs: remove wildcard matcher from root directive (#1513)
* docs: update README with additional documentation links
Add link to classic mode, efficiently serving large static files and monitoring FrankenPHP
Signed-off-by: Romain Bastide <romain.bastide@orange.com>
* ci: combine dependabot updates for one group to 1 pull-request
* feat: compatibility with libphp.dylib on macOS
* feat: upgrade to Caddy 2.10
* feat: upgrade to Caddy 2.10
* chore: run prettier
* fix: build-static.sh consecutive builds (#1496)
* fix consecutive builds
* use minor version in PHP_VERSION
* install jq in centos container
* fix "arm64" download arch for spc binary
* jq is not available as a rpm download
* linter
* specify php 8.4 default
specify 8.4 so we manually switch to 8.5 when we make sure it works
allows to run without jq installed
* Apply suggestions from code review
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
---------
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
* chore: update Go and toolchain version (#1526)
* apply suggestions one be one - scriptpath only
* generate unique worker names by filename and number
* support worker config from embedded apps
* rename back to make sure we don't accidentally add FrankenPHPApp workers to the slice
* fix test after changing error message
* use 🧩 for module workers
* use 🌍 for global workers :)
* revert 1c414cebbc
* revert 4cc8893ced
* apply suggestions
* add dynamic config loading test of module worker
* fix test
* minor changes
---------
Signed-off-by: Romain Bastide <romain.bastide@orange.com>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
Co-authored-by: Indra Gunawan <hello@indra.my.id>
Co-authored-by: Romain Bastide <romain.bastide@orange.com>