Ensure nested CLI components are fresh

This commit is contained in:
udo 2026-07-15 00:43:23 +00:00
parent dafffb4ab6
commit ee022f0398
3 changed files with 50 additions and 6 deletions

View File

@ -146,9 +146,10 @@ a stale entry artifact: they return `503 Service Unavailable` with
`Retry-After: 1`, allowing the client to retry after the priority rebuild.
CLI and explicit compile paths remain synchronous. A failed rebuild removes
availability and surfaces the compiler error rather than serving the old unit
indefinitely. The per-unit lock also keeps concurrent synchronous compilers
from waiting across a transitive graph when a last complete artifact is
available.
indefinitely. This freshness contract includes components resolved lazily from
an otherwise-current CLI entry unit: if the proactive compiler already owns a
stale child's lock, the CLI request joins that compile and waits for the fresh
artifact. Read-only HTTP may use the child's last complete artifact instead.
Before preprocessing, the compiler verifies that the worker can actually read
the source. An unreadable path is reported as a source-read failure with a

View File

@ -21,6 +21,7 @@ mutation_file="/tmp/uce-dependency-mutation-$$"
post_body="/tmp/uce-dependency-post-body-$$"
post_headers="/tmp/uce-dependency-post-headers-$$"
lock_ready_file="/tmp/uce-dependency-lock-ready-$$"
nested_lock_ready_file="/tmp/uce-nested-lock-ready-$$"
race_output="/tmp/uce-source-race-output-$$"
race_error="/tmp/uce-source-race-error-$$"
race_status="/tmp/uce-source-race-status-$$"
@ -31,7 +32,7 @@ cleanup() {
if [[ -n "$cache_dir" ]]; then
rm -rf "$cache_dir"
fi
rm -f "$mutation_file" "$post_body" "$post_headers" "$lock_ready_file" "$race_output" "$race_error" "$race_status"
rm -f "$mutation_file" "$post_body" "$post_headers" "$lock_ready_file" "$nested_lock_ready_file" "$race_output" "$race_error" "$race_status"
}
trap cleanup EXIT
mkdir -p "$source_dir"
@ -175,6 +176,47 @@ if (( elapsed_ms < 2000 )); then
fi
wait "$rebuild_lock_pid"
# CLI freshness applies to every nested component, not only the request entry
# unit. Hold the changed child's compile lock as a proactive rebuild would: the
# parent CLI request must wait and render the new child, never its stale wasm.
printf '%s\n' 'COMPONENT(Request& context) { print("nested-component-marker-a"); }' >"$source_dir/nested-child.uce"
printf '%s\n' \
'CLI(Request& context) { DValue props; print(component("nested-child", props, context)); }' >"$source_dir/nested-parent.uce"
assert_marker nested-parent nested-component-marker-a
nested_child_wasm="$cache_dir/nested-child.uce.wasm"
(
exec 8>"$nested_child_wasm.lock"
flock 8
: >"$nested_lock_ready_file"
sleep 3
) &
nested_lock_pid=$!
deadline=$((SECONDS + 5))
while [[ ! -e "$nested_lock_ready_file" && $SECONDS -lt $deadline ]]; do
if ! kill -0 "$nested_lock_pid" 2>/dev/null; then
echo "nested component lock exited before acquisition" >&2
exit 1
fi
sleep 0.05
done
if [[ ! -e "$nested_lock_ready_file" ]]; then
echo "nested component lock was not acquired before deadline" >&2
exit 1
fi
sed -i 's/nested-component-marker-a/nested-component-marker-b/' "$source_dir/nested-child.uce"
started_at=$(date +%s%N)
nested_output=$(scripts/uce-cli "/$test_name/nested-parent.uce")
elapsed_ms=$(( ($(date +%s%N) - started_at) / 1000000 ))
wait "$nested_lock_pid"
if [[ "$nested_output" != *"nested-component-marker-b"* ]]; then
echo "CLI nested component returned stale wasm: $nested_output" >&2
exit 1
fi
if (( elapsed_ms < 2000 )); then
echo "CLI nested component did not wait for the active rebuild (${elapsed_ms}ms)" >&2
exit 1
fi
# Warm every configured worker, then replace the artifact while retaining its
# whole-second mtime. The worker cache must notice the nanosecond/ctime change.
for _ in {1..16}; do assert_marker parent dependency-marker-d; done

View File

@ -1612,9 +1612,10 @@ private:
}
bool stale = compiler_unit_needs_recompile(context, resolved, 0);
if(stale && compiler_request_can_serve_stale_artifact(context))
bool can_serve_stale = compiler_request_can_serve_stale_artifact(context);
if(stale && can_serve_stale)
compiler_prioritize_unit(context, resolved);
if(!file_exists_host(worker.unit_wasm_path(resolved)) || (stale && !compiler_request_can_serve_stale_artifact(context) && !compiler_unit_compile_in_progress(context, resolved)))
if(!file_exists_host(worker.unit_wasm_path(resolved)) || (stale && !can_serve_stale))
get_shared_unit(context, resolved);
size_t unit_index = 0;