You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
frankenrust-server has no way to run a worker. ServerConfig is {document_root, num_threads, php_ini} (crates/frankenrust-server/src/server.rs:35-39),
boot is an unconditional convert_to_regular_thread loop (:88-93), dispatch
is an unconditional handle_request_with_regular_php_threads(ctx) (:415-418),
and the worker-script override slot in cgi::split_cgi_path is hard-coded None (:373, with cgi.rs:135-136 explicitly deferring it: "The worker
override from cgi.go:203-215 is handled by the worker layer").
Split out of #14, which owns the core park/resume loop and is scoped to frankenrust-core. This issue is the only thing that makes that loop reachable
over HTTP.
What to build
ServerConfig gains worker_script: Option<PathBuf> and num_worker_threads: usize. Add a Default impl and convert every
existing construction site to ..Default::default() so the next field is
not another source-breaking change: crates/frankenrust-server/src/main.rs:36-40, and the five test binaries
under crates/frankenrust-server/tests/ (hello_world.rs, concurrency.rs, directory_index.rs, directory_index_cgi_vars.rs, keepalive_shutdown.rs). This is the one place this issue is allowed to
touch those files; do not change what they assert.
The boot loop splits (server.rs:88-93): num_worker_threads claims
become convert_to_worker_thread(claim, worker) (core: worker mode -- the frankenphp_handle_request park/resume loop #14's), the remainder stay
regular. Reject a config with num_worker_threads > 0 and no worker_script, and one where the two counts exceed num_threads, with a
real error rather than a panic — upstream's own count validation is in worker.go.
Dispatch routes (server.rs:415-418): if a worker matches the resolved
script, hand the RequestContext to core: worker mode -- the frankenphp_handle_request park/resume loop #14's worker dispatch instead of handle_request_with_regular_php_threads. Resolution is by script path only
(worker.go's workersByPath); named-worker routing and m#-prefixed
module workers are out of scope.
split_cgi_path's worker override (server.rs:373 currently None):
pass the worker's file name, porting cgi.go:203-215.
main.rs reads the knobs: FRANKENRUST_WORKER_SCRIPT and FRANKENRUST_WORKER_THREADS, matching the existing FRANKENRUST_DOCUMENT_ROOT/FRANKENRUST_THREADS style.
docker/frankenrust.Dockerfile passes both through, alongside the
existing FRANKENRUST_LISTEN/FRANKENRUST_THREADS (:100, :120), so the
conformance and bench legs can drive worker mode later.
Acceptance
crates/frankenrust-server/tests/worker_hello.rs (new binary — one init_php_threads per process, see hello_world.rs's module doc): boots
with worker_script set to a fixture using the _executor.php pattern,
issues one request, asserts status/body match what the same fixture returns
in regular mode.
crates/frankenrust-server/tests/worker_persistence.rs (new binary): num_threads: 1, num_worker_threads: 1, N sequential HTTP requests against vendor/frankenphp/testdata/worker-with-counter.php, asserting bodies requests:1 .. requests:N. This is the test a server that silently
re-bootstraps per request fails. (core: worker mode -- the frankenphp_handle_request park/resume loop #14 pins the same property below the HTTP
layer; this pins it through it.)
A test that a worker_script config still serves a non-worker script in
the same document root through the regular path.
bash scripts/gate.sh default passes.
Out of scope
max_requests and the thread reboot it triggers, autoscaling, the file
watcher and RestartWorkers, worker-mode sessions, metrics.
Named workers, m# module workers, SendMessage/extension workers.
Multiple workers in one server — one worker_script is enough; make the
registry able to hold more than one without exposing a way to configure it.
Gate: default
Agent: claude
Depends on: #14, #207
frankenrust-serverhas no way to run a worker.ServerConfigis{document_root, num_threads, php_ini}(crates/frankenrust-server/src/server.rs:35-39),boot is an unconditional
convert_to_regular_threadloop (:88-93), dispatchis an unconditional
handle_request_with_regular_php_threads(ctx)(:415-418),and the worker-script override slot in
cgi::split_cgi_pathis hard-codedNone(:373, withcgi.rs:135-136explicitly deferring it: "The workeroverride from
cgi.go:203-215is handled by the worker layer").Split out of #14, which owns the core park/resume loop and is scoped to
frankenrust-core. This issue is the only thing that makes that loop reachableover HTTP.
What to build
ServerConfiggainsworker_script: Option<PathBuf>andnum_worker_threads: usize. Add aDefaultimpl and convert everyexisting construction site to
..Default::default()so the next field isnot another source-breaking change:
crates/frankenrust-server/src/main.rs:36-40, and the five test binariesunder
crates/frankenrust-server/tests/(hello_world.rs,concurrency.rs,directory_index.rs,directory_index_cgi_vars.rs,keepalive_shutdown.rs). This is the one place this issue is allowed totouch those files; do not change what they assert.
server.rs:88-93):num_worker_threadsclaimsbecome
convert_to_worker_thread(claim, worker)(core: worker mode -- the frankenphp_handle_request park/resume loop #14's), the remainder stayregular. Reject a config with
num_worker_threads > 0and noworker_script, and one where the two counts exceednum_threads, with areal error rather than a panic — upstream's own count validation is in
worker.go.FRANKENPHP_WORKER=1in its prepared env(
worker.go:138), through core: the prepared environment -- RequestContext.env, registerPreparedEnv, and the $_SERVER merge #207'sRequestContext::env. Without this,vendor/frankenphp/testdata/_executor.php:4takes the regular-mode branchand the worker script never calls
frankenphp_handle_request()— which core: worker mode -- the frankenphp_handle_request park/resume loop #14'sboot detection then correctly reports as a boot failure, in a quadratic
backoff loop, while the HTTP request hangs. This is the failure this issue
exists to prevent; it is not optional.
server.rs:415-418): if a worker matches the resolvedscript, hand the
RequestContextto core: worker mode -- the frankenphp_handle_request park/resume loop #14's worker dispatch instead ofhandle_request_with_regular_php_threads. Resolution is by script path only(
worker.go'sworkersByPath); named-worker routing andm#-prefixedmodule workers are out of scope.
split_cgi_path's worker override (server.rs:373currentlyNone):pass the worker's file name, porting
cgi.go:203-215.main.rsreads the knobs:FRANKENRUST_WORKER_SCRIPTandFRANKENRUST_WORKER_THREADS, matching the existingFRANKENRUST_DOCUMENT_ROOT/FRANKENRUST_THREADSstyle.docker/frankenrust.Dockerfilepasses both through, alongside theexisting
FRANKENRUST_LISTEN/FRANKENRUST_THREADS(:100,:120), so theconformance and bench legs can drive worker mode later.
Acceptance
crates/frankenrust-server/tests/worker_hello.rs(new binary — oneinit_php_threadsper process, seehello_world.rs's module doc): bootswith
worker_scriptset to a fixture using the_executor.phppattern,issues one request, asserts status/body match what the same fixture returns
in regular mode.
crates/frankenrust-server/tests/worker_persistence.rs(new binary):num_threads: 1,num_worker_threads: 1, N sequential HTTP requests againstvendor/frankenphp/testdata/worker-with-counter.php, asserting bodiesrequests:1..requests:N. This is the test a server that silentlyre-bootstraps per request fails. (core: worker mode -- the frankenphp_handle_request park/resume loop #14 pins the same property below the HTTP
layer; this pins it through it.)
worker_scriptconfig still serves a non-worker script inthe same document root through the regular path.
bash scripts/gate.sh defaultpasses.Out of scope
max_requestsand the thread reboot it triggers, autoscaling, the filewatcher and
RestartWorkers, worker-mode sessions, metrics.m#module workers,SendMessage/extension workers.worker_scriptis enough; make theregistry able to hold more than one without exposing a way to configure it.