In #2205 it appears that workers could be reported in metrics as "ready"
before they are actually ready. This changes the reporting so that
workers are only reported ready once they have completed booting.
Signed-off-by: Robert Landers <landers.robert@gmail.com>
This one is interesting — though I’m not sure the best way to provide a
test. I will have to look into maybe an integration test because it is a
careful dance between how we resolve paths in the Caddy module vs.
workers. I looked into making a proper change (literally using the same
logic everywhere), but I think it is best to wait until #1646 is merged.
But anyway, this change deals with some interesting edge cases. I will
use gherkin to describe them:
```gherkin
Feature: FrankenPHP symlinked edge cases
Background:
Given a `test` folder
And a `public` folder linked to `test`
And a worker script located at `test/index.php`
And a `test/nested` folder
And a worker script located at `test/nested/index.php`
Scenario: neighboring worker script
Given frankenphp located in the test folder
When I execute `frankenphp php-server --listen localhost:8080 -w index.php` from `public`
Then I expect to see the worker script executed successfully
Scenario: nested worker script
Given frankenphp located in the test folder
When I execute `frankenphp --listen localhost:8080 -w nested/index.php` from `public`
Then I expect to see the worker script executed successfully
Scenario: outside the symlinked folder
Given frankenphp located in the root folder
When I execute `frankenphp --listen localhost:8080 -w public/index.php` from the root folder
Then I expect to see the worker script executed successfully
Scenario: specified root directory
Given frankenphp located in the root folder
When I execute `frankenphp --listen localhost:8080 -w public/index.php -r public` from the root folder
Then I expect to see the worker script executed successfully
```
Trying to write that out in regular English would be more complex IMHO.
These scenarios should all pass now with this PR.
---------
Signed-off-by: Marc <m@pyc.ac>
Co-authored-by: henderkes <m@pyc.ac>
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>
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
* 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>
* Decouple workers.
* Moves code to separate file.
* Cleans up the exponential backoff.
* Initial working implementation.
* Refactors php threads to take callbacks.
* Cleanup.
* Cleanup.
* Cleanup.
* Cleanup.
* Adjusts watcher logic.
* Adjusts the watcher logic.
* Fix opcache_reset race condition.
* Fixing merge conflicts and formatting.
* Prevents overlapping of TSRM reservation and script execution.
* Adjustments as suggested by @dunglas.
* Adds error assertions.
* Adds comments.
* Removes logs and explicitly compares to C.false.
* Resets check.
* Adds cast for safety.
* Fixes waitgroup overflow.
* Resolves waitgroup race condition on startup.
* Moves worker request logic to worker.go.
* Removes defer.
* Removes call from go to c.
* Fixes merge conflict.
* Adds fibers test back in.
* Refactors new thread loop approach.
* Removes redundant check.
* Adds compareAndSwap.
* Refactor: removes global waitgroups and uses a 'thread state' abstraction instead.
* Removes unnecessary method.
* Updates comment.
* Removes unnecessary booleans.
* test
* First state machine steps.
* Splits threads.
* Minimal working implementation with broken tests.
* Fixes tests.
* Refactoring.
* Fixes merge conflicts.
* Formatting
* C formatting.
* More cleanup.
* Allows for clean state transitions.
* Adds state tests.
* Adds support for thread transitioning.
* Fixes the testdata path.
* Formatting.
* Allows transitioning back to inactive state.
* Fixes go linting.
* Formatting.
* Removes duplication.
* Applies suggestions by @dunglas
* Removes redundant check.
* Locks the handler on restart.
* Removes unnecessary log.
* Changes Unpin() logic as suggested by @withinboredom
* Adds suggestions by @dunglas and resolves TODO.
* Makes restarts fully safe.
* Will make the initial startup fail even if the watcher is enabled (as is currently the case)
* Also adds compareAndSwap to the test.
* Adds comment.
* Prevents panic on initial watcher startup.
* implement getenv and putenv in go
* fix typo
* apply formatting
* return a bool
* prevent ENV= from crashing
* optimization
* optimization
* split env workflows and use go_strings
* clean up unused code
* update tests
* remove useless sprintf
* see if this fixes the asan issues
* clean up comments
* check that VAR= works correctly and use actual php to validate the behavior
* move all unpinning to the end of the request
* handle the case where php is not installed
* fix copy-paste
* optimization
* use strings.cut
* fix lint
* override how env is filled
* reuse fullenv
* use corect function
* Removes the worker panic when the watcher is enabled.
* Only panics on the initial boot.
* Only panics on the initial boot.
* Resets to always panic when watcher is disabled.
---------
Co-authored-by: a.stecher <a.stecher@sportradar.com>
* handle failures gracefully
* fix super-subtle race condition
* address feedback: panic instead of fatal log and make vars into consts
* pass the frankenphp context to worker-ready function
* reset backoff and failures on normal restart
* update docs
* add test and fix race condition
* fail sometimes but do not be pathological about it
* Use title case
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
* fix code style in php
* define lifecycle metrics
* ensure we update unregister the metrics and fix tests
* update caddy tests and fix typo
* update docs
* no need for this
---------
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
* add metrics
* change how counting works
* also replace dots
* check that metrics exist
* rename NullMetrics to nullMetrics
* update go.sum
* register collectors only once
* add tests
* add tests for metrics and fix bugs
* keep old metrics around for test
* properly reset during shutdown
* use the same method as frankenphp
* Revert "keep old metrics around for test"
This reverts commit 1f0df6f6bdaebf32aec346f068d6f42a0b5f4007.
* change to require.NoError
* compile regex only once
* remove name sanitizer
* use require
* parameterize host port because security software sucks
* remove need for renaming workers
* increase number of threads and add tests
* fix where frankenphp configuration was bleeding into later tests
* adds basic docs for metrics
* Add caddy metrics link
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
* Fix typos
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
* address feedback
* change comment to be much more "dangerous"
---------
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>