Exclude cold module loads from guest deadlines
This commit is contained in:
parent
73a2671112
commit
f7e9c5a4ec
@ -131,6 +131,10 @@ available. If an artifact is cold or stale, dispatch compiles it on demand via
|
||||
Workers identify cached unit artifacts by nanosecond mtime, ctime, and size.
|
||||
Whole-second mtime alone is insufficient because a dependency-triggered rebuild
|
||||
can replace a wasm artifact within the same second as its prior build.
|
||||
Cold module compilation and deserialization are host work, so `load_unit()`
|
||||
refreshes the epoch deadline before its first guest call. Otherwise a component
|
||||
whose compilation outlasted the guest CPU budget would immediately trap in the
|
||||
following allocator/relocation call even though no guest loop consumed it.
|
||||
|
||||
---
|
||||
|
||||
@ -330,9 +334,11 @@ header free-functions are `inline`. The wasm backend exposes only declarations
|
||||
`scripts/test_dependency_invalidation.sh`. The latter changes a transitive
|
||||
`#load`, then replaces a warmed worker artifact while preserving its
|
||||
whole-second mtime to prove both compiler and worker caches invalidate it.
|
||||
`scripts/test_cold_component_deadline.sh` separately compiles a deliberately
|
||||
cold component that exceeds the development epoch window and proves the
|
||||
parent request still renders it.
|
||||
|
||||
- **WebSocket end-to-end**: a headless client performs a raw WS handshake to
|
||||
`:HTTP_PORT` with path `/site/tests/websockets.ws.uce` (self-resolving
|
||||
`SCRIPT_FILENAME`) and asserts the `hello-ack` frame — exercising the full
|
||||
broker → worker → broker → client chain across process boundaries.
|
||||
|
||||
|
||||
@ -51,4 +51,5 @@ url="http://localhost/tests/cli_runner.uce?action=${action}&include_kill=${inclu
|
||||
curl -sS --fail-with-body --unix-socket "$socket_path" "$url"
|
||||
if [[ "$action" == "run" ]]; then
|
||||
scripts/test_dependency_invalidation.sh
|
||||
scripts/test_cold_component_deadline.sh
|
||||
fi
|
||||
|
||||
40
scripts/test_cold_component_deadline.sh
Executable file
40
scripts/test_cold_component_deadline.sh
Executable file
@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
test_name="component-deadline-test-$$"
|
||||
site_directory="${UCE_TEST_SITE_DIRECTORY:-site}"
|
||||
source_dir="$site_directory/$test_name"
|
||||
bin_directory="${BIN_DIRECTORY:-}"
|
||||
if [[ -z "$bin_directory" && -r /etc/uce/settings.cfg ]]; then
|
||||
bin_directory=$(awk -F= '/^[[:space:]]*BIN_DIRECTORY[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2; exit}' /etc/uce/settings.cfg)
|
||||
fi
|
||||
bin_directory="${bin_directory:-/tmp/uce/work}"
|
||||
cache_dir=""
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$source_dir"
|
||||
if [[ -n "$cache_dir" ]]; then
|
||||
rm -rf "$cache_dir"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
mkdir -p "$source_dir"
|
||||
cache_dir="$bin_directory$(realpath "$source_dir")"
|
||||
|
||||
printf '%s\n' \
|
||||
'CLI(Request& context) { DValue props; print(component("child", props, context)); }' >"$source_dir/parent.uce"
|
||||
for i in $(seq 1 800); do
|
||||
printf 'String cold_component_pad_%s() { return("%s"); }\n' "$i" "$i"
|
||||
done >"$source_dir/child.uce"
|
||||
printf '%s\n' \
|
||||
'COMPONENT(Request& context) { <><strong>cold-component-deadline-ok</strong></> }' >>"$source_dir/child.uce"
|
||||
rm -rf "$cache_dir"
|
||||
|
||||
output=$(scripts/uce-cli "/$test_name/parent.uce")
|
||||
if [[ "$output" != *cold-component-deadline-ok* ]]; then
|
||||
echo "cold component compilation consumed the guest epoch budget: $output" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "cold component deadline passed"
|
||||
@ -1190,6 +1190,10 @@ private:
|
||||
auto mod = worker.unit_module(source_path, error);
|
||||
if(!mod)
|
||||
return(error);
|
||||
// Compiling/deserializing a cold module is host work. Refresh the guest
|
||||
// watchdog before the first core call so that wall time cannot make the
|
||||
// otherwise harmless malloc/reloc sequence trap immediately.
|
||||
ctx().set_epoch_deadline(worker.cfg.epoch_deadline_ticks);
|
||||
if(mod->abi.version != abi_version)
|
||||
return(mod->wasm_path + ": uce.abi version " + std::to_string(mod->abi.version)
|
||||
+ " does not match core ABI " + std::to_string(abi_version));
|
||||
@ -1347,12 +1351,8 @@ private:
|
||||
if(worker.cfg.verbose)
|
||||
fprintf(stderr, "[wasm] loaded %s (mem_base=%u table_base=%u)\n",
|
||||
source_path.c_str(), memory_base, table_base);
|
||||
// The epoch budget is a guest-CPU watchdog, but the ticker is wall-clock
|
||||
// and host-side module compilation here (lazy, mid-render, possibly many
|
||||
// units) burns it without the guest running. Reset the deadline after a
|
||||
// load so the budget measures guest execution between membrane crossings,
|
||||
// not our compile time. A genuine runaway loop makes no loads, so it
|
||||
// still trips the deadline.
|
||||
// Exclude the rest of host-side loading as well. A genuine runaway loop
|
||||
// makes no loads, so it still trips the deadline.
|
||||
ctx().set_epoch_deadline(worker.cfg.epoch_deadline_ticks);
|
||||
return("");
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user