Keep warm wasm workers alive
This commit is contained in:
@@ -159,6 +159,13 @@ suspend the native SIGSEGV/SIGILL recovery handler around the wasm call so that
|
|||||||
Wasmtime's own trap signals are not escalated into a native fatal signal (see
|
Wasmtime's own trap signals are not escalated into a native fatal signal (see
|
||||||
`serve_via_wasm` in `handle_complete`).
|
`serve_via_wasm` in `handle_complete`).
|
||||||
|
|
||||||
|
Render workers are long-lived. The generic FastCGI transport retains a legacy
|
||||||
|
eight-connection recycle default, but `listen_for_connections()` disables it
|
||||||
|
for the UCE pool: recycling a healthy worker discards its Wasmtime engine and
|
||||||
|
module cache, adding a visible cold-start request. Request state remains bounded
|
||||||
|
by the fresh workspace and the normal per-request database/resource cleanup;
|
||||||
|
faulted workers still exit and are replaced by the parent.
|
||||||
|
|
||||||
### Task callbacks and workspace lifetime
|
### Task callbacks and workspace lifetime
|
||||||
|
|
||||||
`task()` and `task_repeat()` are fork-backed. The `uce_host_task_spawn` hostcall
|
`task()` and `task_repeat()` are fork-backed. The `uce_host_task_spawn` hostcall
|
||||||
@@ -333,7 +340,10 @@ header free-functions are `inline`. The wasm backend exposes only declarations
|
|||||||
in-runtime CLI suite (`site/tests/cli_runner.uce`) plus the site test pages and
|
in-runtime CLI suite (`site/tests/cli_runner.uce`) plus the site test pages and
|
||||||
`scripts/test_dependency_invalidation.sh`. The latter changes a transitive
|
`scripts/test_dependency_invalidation.sh`. The latter changes a transitive
|
||||||
`#load`, then replaces a warmed worker artifact while preserving its
|
`#load`, then replaces a warmed worker artifact while preserving its
|
||||||
whole-second mtime to prove both compiler and worker caches invalidate it.
|
whole-second mtime to prove both compiler and worker caches invalidate it. It
|
||||||
|
also sends 48 requests and asserts the observed worker PID set does not exceed
|
||||||
|
`WORKER_COUNT`, guarding against accidental reintroduction of request-count
|
||||||
|
recycling.
|
||||||
`scripts/test_cold_component_deadline.sh` separately compiles a deliberately
|
`scripts/test_cold_component_deadline.sh` separately compiles a deliberately
|
||||||
cold component that exceeds the development epoch window and proves the
|
cold component that exceeds the development epoch window and proves the
|
||||||
parent request still renders it.
|
parent request still renders it.
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ printf '%s\n' \
|
|||||||
'#endif' >"$source_dir/child.uce"
|
'#endif' >"$source_dir/child.uce"
|
||||||
printf '%s\n' \
|
printf '%s\n' \
|
||||||
'#load "child.uce"' \
|
'#load "child.uce"' \
|
||||||
'CLI(Request& context) { print(dependency_cache_marker()); }' \
|
'CLI(Request& context) { print(dependency_cache_marker(), ":", request_perf()["worker_pid"].to_string()); }' \
|
||||||
'RENDER(Request& context) { print(dependency_cache_marker()); }' >"$source_dir/parent.uce"
|
'RENDER(Request& context) { print(dependency_cache_marker(), ":", request_perf()["worker_pid"].to_string()); }' >"$source_dir/parent.uce"
|
||||||
|
|
||||||
assert_marker() {
|
assert_marker() {
|
||||||
local path="$1"
|
local path="$1"
|
||||||
@@ -62,4 +62,17 @@ touch -d "@$parent_mtime" "$parent_wasm"
|
|||||||
rm -f "$cache_dir/parent.uce.cwasm"
|
rm -f "$cache_dir/parent.uce.cwasm"
|
||||||
for _ in {1..16}; do assert_marker parent dependency-marker-c; done
|
for _ in {1..16}; do assert_marker parent dependency-marker-c; done
|
||||||
|
|
||||||
|
worker_count=$(awk -F= '/^[[:space:]]*WORKER_COUNT[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2; exit}' /etc/uce/settings.cfg 2>/dev/null || true)
|
||||||
|
worker_count="${worker_count:-4}"
|
||||||
|
worker_pids=""
|
||||||
|
for _ in {1..48}; do
|
||||||
|
output=$(scripts/uce-cli "/$test_name/parent.uce")
|
||||||
|
worker_pids+="${output##*:}"$'\n'
|
||||||
|
done
|
||||||
|
unique_workers=$(printf '%s' "$worker_pids" | sed '/^$/d' | sort -u | wc -l)
|
||||||
|
if (( unique_workers > worker_count )); then
|
||||||
|
echo "worker pool recycled during 48 requests: $unique_workers PIDs for $worker_count workers" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "dependency invalidation passed"
|
echo "dependency invalidation passed"
|
||||||
|
|||||||
@@ -1352,6 +1352,10 @@ void listen_for_connections()
|
|||||||
// Workers are uniform FastCGI/CLI renderers; the WS broker owns the HTTP/WS
|
// Workers are uniform FastCGI/CLI renderers; the WS broker owns the HTTP/WS
|
||||||
// port and every connection, so workers never accept raw HTTP themselves.
|
// port and every connection, so workers never accept raw HTTP themselves.
|
||||||
server.close_http_listeners();
|
server.close_http_listeners();
|
||||||
|
// The transport's legacy eight-connection recycle predates persistent
|
||||||
|
// Wasmtime engines. Recycling makes every ninth request pay engine/module
|
||||||
|
// startup; request-scoped workspaces already isolate and release user state.
|
||||||
|
server.calls_until_termination = -1;
|
||||||
server.on_request = &handle_request;
|
server.on_request = &handle_request;
|
||||||
server.on_data = &handle_data;
|
server.on_data = &handle_data;
|
||||||
server.on_complete = &handle_complete;
|
server.on_complete = &handle_complete;
|
||||||
|
|||||||
Reference in New Issue
Block a user