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
+4 -3
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. `Retry-After: 1`, allowing the client to retry after the priority rebuild.
CLI and explicit compile paths remain synchronous. A failed rebuild removes CLI and explicit compile paths remain synchronous. A failed rebuild removes
availability and surfaces the compiler error rather than serving the old unit availability and surfaces the compiler error rather than serving the old unit
indefinitely. The per-unit lock also keeps concurrent synchronous compilers indefinitely. This freshness contract includes components resolved lazily from
from waiting across a transitive graph when a last complete artifact is an otherwise-current CLI entry unit: if the proactive compiler already owns a
available. 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 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 the source. An unreadable path is reported as a source-read failure with a
+43 -1
View File
@@ -21,6 +21,7 @@ mutation_file="/tmp/uce-dependency-mutation-$$"
post_body="/tmp/uce-dependency-post-body-$$" post_body="/tmp/uce-dependency-post-body-$$"
post_headers="/tmp/uce-dependency-post-headers-$$" post_headers="/tmp/uce-dependency-post-headers-$$"
lock_ready_file="/tmp/uce-dependency-lock-ready-$$" 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_output="/tmp/uce-source-race-output-$$"
race_error="/tmp/uce-source-race-error-$$" race_error="/tmp/uce-source-race-error-$$"
race_status="/tmp/uce-source-race-status-$$" race_status="/tmp/uce-source-race-status-$$"
@@ -31,7 +32,7 @@ cleanup() {
if [[ -n "$cache_dir" ]]; then if [[ -n "$cache_dir" ]]; then
rm -rf "$cache_dir" rm -rf "$cache_dir"
fi 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 trap cleanup EXIT
mkdir -p "$source_dir" mkdir -p "$source_dir"
@@ -175,6 +176,47 @@ if (( elapsed_ms < 2000 )); then
fi fi
wait "$rebuild_lock_pid" 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 # Warm every configured worker, then replace the artifact while retaining its
# whole-second mtime. The worker cache must notice the nanosecond/ctime change. # whole-second mtime. The worker cache must notice the nanosecond/ctime change.
for _ in {1..16}; do assert_marker parent dependency-marker-d; done for _ in {1..16}; do assert_marker parent dependency-marker-d; done
+3 -2
View File
@@ -1612,9 +1612,10 @@ private:
} }
bool stale = compiler_unit_needs_recompile(context, resolved, 0); 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); 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); get_shared_unit(context, resolved);
size_t unit_index = 0; size_t unit_index = 0;