feat: declared background workers + frankenphp_get_worker_handle()#2543
Open
nicolas-grekas wants to merge 1 commit into
Open
feat: declared background workers + frankenphp_get_worker_handle()#2543nicolas-grekas wants to merge 1 commit into
nicolas-grekas wants to merge 1 commit into
Conversation
Background workers are long-lived non-HTTP PHP scripts declared via WithWorkerBackground() or the `background` flag in Caddyfile worker blocks. The script runs in a loop: it is re-run on cooperative exit (status 0) and restarted with a quadratic backoff on crash, failing hard on max_consecutive_failures during startup only. frankenphp_get_worker_handle() returns a stream over the read end of a per-thread stop pipe; draining the thread (shutdown, reboot, handler transition) closes the write end, so a script parked in stream_select wakes up and can exit gracefully. Background workers attach to a Server through the existing WithWorkerServerScope(); their name is mandatory (it is the script's identity, exposed as FRANKENPHP_WORKER) and lives in the global worker namespace, which the Caddy module already qualifies per server.
Contributor
Author
|
Should be considered after #2499 is merged of course! |
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rewrite of #2398 on top of #2499, targeting the
refactor/phpserverbranch:the smallest useful slice of the background-worker work, now built on
Serverand the server names merged in #2529 instead of a parallelScopemechanism. A single commit on top of the base branch.
A worker declared with
WithWorkerBackground()/backgroundin theCaddyfile runs its script in a loop outside the HTTP request cycle. The
frankenphp_get_worker_handle()primitive hands the script a stream thatreaches EOF when FrankenPHP drains the worker, so it can park on
stream_select()and exit gracefully on shutdown, reboot or restart.What's in
frankenphp_get_worker_handle(): resource, closes when theworker is drained (the Go side closes the write end of a per-thread stop
pipe).
WithWorkerBackground(). Background workers attach to aServerthrough the existingWithWorkerServerScope();num >= 1isrequired (no lazy-start in this build) and the name is mandatory since it
is the script's identity (
$_SERVER['FRANKENPHP_WORKER']).backgroundWorkerThreadimplementsthreadHandlerandmirrors
workerThread's state machine: boot, re-run on cooperative exit(status 0, backoff reset), crash-restart with quadratic backoff, hard
failure on
max_consecutive_failuresduring startup only.backgroundflag inside worker blocks (php_server and global).nameis required,matchis rejected. Metrics/logs use the worker name,which feat: name Server instances and attribute workers to them #2529's qualification already keeps unique across
php_serverblocks, so two blocks can declare the same worker name.
What the rewrite changes vs #2398
Building on
Serverdeletes the parallel machinery the old PR carried andaddresses all review comments:
Scope,NextScope,SetScopeLabel, thescopeOwnersregistry andcaddy/scopelabel.goare gone;Serverand the feat: name Server instances and attribute workers to them #2529 name resolutionreplace them. This also removes the reported
scopeOwnersdata race andthe
m#<label>:m#<name>double-prefix in metric names. The feature diffis ~250 lines smaller than feat: declared background workers + frankenphp_get_worker_handle() #2398.
php_streamin a TLS slot: eachfrankenphp_get_worker_handle()call dups the read fd into a freshstream owned by its zval. No
GC_ADDREFwithout release, no danglingpointer across restarts or thread recycling, nothing to clean up in
frankenphp_update_local_thread_context()beyond the raw fds.-1return offrankenphp_set_background_worker_and_get_stop_fd_write()is handled:
Init()fails with a clear error during startup; paststartup the thread backs off and retries instead of letting the script
crash-loop on
frankenphp_get_worker_handle()exceptions.implementation.
drain()(thethreadHandlerseam that is declared but not called yeton this branch) is wired into
thread.shutdown()andrebootAllThreads(), so shutdowns and watch-triggered reboots wakeparked background workers instead of hitting the force-kill grace period.
frankenphp_handle_request()throws when called from a background worker (previously it would have hit
the
.(*workerThread)type assertion and crashed the process), andWithWorkerName()rejects requests targeting a background worker.ready once the script starts, and every stop path emits
StopWorker), sothe
readyWorkersgauge can't drift negative.Deferred (kept out for review surface)
frankenphp_ensure_background_worker()and lazy-start machinery.frankenphp_set_vars/frankenphp_get_vars).frankenphp_start_background_worker()runtime API(discussed in feat: declared background workers + frankenphp_get_worker_handle() #2398); the primitives here are compatible with it.
Test plan
TestBackgroundWorkerLifecycle: bg worker boots, touches sentinel,parks on the stop pipe,
Shutdown()returns within 10s.TestBackgroundWorkerCrashRestarts:exit(1)on first boot, therespawned run touches the "restarted" sentinel.
TestBackgroundWorkerOnServer: server-scoped worker inherits theserver env,
FRANKENPHP_WORKERcarries the name, HTTP requests on thesame server still serve.
TestBackgroundWorkerValidation: name required,num >= 1, globalname namespace, request matchers rejected.
TestWorkerBackgroundConfig/RequiresName/RejectsMatch(Caddyfile parsing, pass locally).
The runtime tests need CI to confirm: on my current box (WSL2), per-thread
engine bootstrap of any embed ZTS build is pathologically slow, so every
Init()-based test times out locally, including on unmodified base commits.