Bound request-time Wasm compilation
This commit is contained in:
@@ -72,6 +72,7 @@ if [[ "$action" == "run" ]]; then
|
||||
timeout --signal=TERM --kill-after=5s 175s scripts/test_parallel_proactive_compile.sh
|
||||
scripts/test_cold_component_deadline.sh
|
||||
scripts/test_wasm_invocation_timeout.sh
|
||||
timeout --signal=TERM --kill-after=5s 130s scripts/test_wasm_compile_timeout.sh
|
||||
scripts/test_nested_component_props.sh
|
||||
scripts/test_component_once_prefetch.sh
|
||||
scripts/test_relative_component_cache.sh
|
||||
|
||||
@@ -398,7 +398,7 @@ rm -f "$cache_dir/parent.uce.cwasm"
|
||||
for _ in {1..16}; do assert_marker parent dependency-marker-c; done
|
||||
|
||||
race_unit="$source_dir/source-race.uce"
|
||||
race_cpp="$cache_dir/source-race.uce.cpp"
|
||||
race_cpp=""
|
||||
{
|
||||
printf '%s\n' 'String source_race_marker() { return("source-race-marker-a"); }'
|
||||
for i in $(seq 1 3500); do
|
||||
@@ -414,16 +414,17 @@ rm -f "$cache_dir/source-race.uce.cpp" "$cache_dir/source-race.uce.wasm" "$cache
|
||||
) &
|
||||
race_pid=$!
|
||||
deadline=$((SECONDS + 10))
|
||||
while [[ ! -e "$race_cpp" && $SECONDS -lt $deadline ]]; do
|
||||
while [[ -z "$race_cpp" && $SECONDS -lt $deadline ]]; do
|
||||
race_cpp=$(find "$cache_dir" -maxdepth 1 -name 'source-race.uce.invocation-*.cpp' -print -quit 2>/dev/null || true)
|
||||
if ! kill -0 "$race_pid" 2>/dev/null; then
|
||||
echo "source-race compile finished before publishing generated C++" >&2
|
||||
echo "source-race compile finished before staging generated C++" >&2
|
||||
cat "$race_output" "$race_error" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
sleep 0.02
|
||||
done
|
||||
if [[ ! -e "$race_cpp" ]]; then
|
||||
echo "source-race compile did not publish generated C++ before deadline" >&2
|
||||
if [[ -z "$race_cpp" || ! -e "$race_cpp" ]]; then
|
||||
echo "source-race compile did not stage generated C++ before deadline" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q 'source-race-marker-a' "$race_cpp"; then
|
||||
|
||||
Executable
+206
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
error_page_path=$(sed -n '/^bool render_wasm_error_page(/,/^}/p' src/linux_fastcgi.cpp)
|
||||
[[ "$error_page_path" == *'get_shared_unit_bounded('* && "$error_page_path" == *'wasm_invocation_remaining_ms(invocation_deadline)'* ]]
|
||||
[[ "$error_page_path" != *'get_shared_unit(&request'* ]]
|
||||
|
||||
if [[ "${1:-}" != --inside ]]; then
|
||||
exec timeout --signal=TERM --kill-after=5s 120s unshare --mount --fork --kill-child=TERM "$0" --inside
|
||||
fi
|
||||
|
||||
root="/tmp/uce-compile-timeout-$$"
|
||||
site="$root/site"
|
||||
work="$root/work"
|
||||
settings="$root/settings.cfg"
|
||||
log="$root/service.log"
|
||||
server_pid=""
|
||||
|
||||
cleanup() {
|
||||
status=$?
|
||||
if [[ -n "$server_pid" ]] && kill -0 "$server_pid" 2>/dev/null; then
|
||||
kill -TERM "$server_pid" 2>/dev/null || true
|
||||
for _ in $(seq 1 100); do kill -0 "$server_pid" 2>/dev/null || break; sleep 0.02; done
|
||||
kill -KILL "$server_pid" 2>/dev/null || true
|
||||
wait "$server_pid" 2>/dev/null || true
|
||||
fi
|
||||
if (( status != 0 )) && [[ -r "$log" ]]; then cat "$log" >&2; fi
|
||||
rm -rf "$root"
|
||||
return "$status"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
mkdir -p "$site/components" "$work" "$root/run" "$root/session" "$root/upload"
|
||||
sed -E '/^[[:space:]]*(BIN_DIRECTORY|PRECOMPILE_FILES_IN|SITE_DIRECTORY|FCGI_SOCKET_PATH|FCGI_PORT|CLI_SOCKET_PATH|WS_BROKER_SOCKET_PATH|HTTP_PORT|HTTP_DOCUMENT_ROOT|SESSION_PATH|TMP_UPLOAD_PATH|WASM_CORE_PATH|WASM_COMPILE_SCRIPT|WASM_INVOCATION_TIMEOUT_MS|WASM_EPOCH_PERIOD_MS|PROACTIVE_COMPILE_ENABLED|WORKER_COUNT|COMPILE_FAILURE_RETRY_SECONDS)[[:space:]]*=/d' \
|
||||
/etc/uce/settings.cfg >"$settings"
|
||||
cat >>"$settings" <<CFG
|
||||
BIN_DIRECTORY=$work
|
||||
PRECOMPILE_FILES_IN=$site
|
||||
SITE_DIRECTORY=$site
|
||||
FCGI_SOCKET_PATH=$root/run/fastcgi.sock
|
||||
FCGI_PORT=
|
||||
CLI_SOCKET_PATH=$root/run/cli.sock
|
||||
WS_BROKER_SOCKET_PATH=$root/run/ws.sock
|
||||
HTTP_PORT=
|
||||
HTTP_DOCUMENT_ROOT=$site
|
||||
SESSION_PATH=$root/session
|
||||
TMP_UPLOAD_PATH=$root/upload
|
||||
WASM_CORE_PATH=$(pwd)/bin/wasm/core.wasm
|
||||
WASM_COMPILE_SCRIPT=$root/compile
|
||||
WASM_INVOCATION_TIMEOUT_MS=2000
|
||||
WASM_EPOCH_PERIOD_MS=20
|
||||
PROACTIVE_COMPILE_ENABLED=0
|
||||
WORKER_COUNT=1
|
||||
COMPILE_FAILURE_RETRY_SECONDS=60
|
||||
CFG
|
||||
mount --bind "$settings" /etc/uce/settings.cfg
|
||||
|
||||
cat >"$root/compile" <<SHIM
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
source_file="\$3"
|
||||
if [[ -r '$root/stage-timeout' ]] && grep -Fxq "\$source_file" '$root/stage-timeout'; then
|
||||
printf '%s\n' "\$\$" >'$root/compiler-pid'
|
||||
(sleep 3; touch '$root/late-descendant') &
|
||||
cp "\$2/\$(basename "\$source_file").wasm" "\$2/\$5"
|
||||
cp "\$2/\$(basename "\$source_file").wasm.source-map" "\$2/\$5.source-map"
|
||||
sleep 60
|
||||
fi
|
||||
if [[ -r '$root/hold' ]] && grep -Fxq "\$source_file" '$root/hold'; then
|
||||
printf '%s\n' "\$\$" >'$root/compiler-pid'
|
||||
(sleep 3; touch '$root/late-descendant') &
|
||||
sleep 60
|
||||
fi
|
||||
if [[ -r '$root/silent-fail' ]] && grep -Fxq "\$source_file" '$root/silent-fail'; then exit 23; fi
|
||||
if [[ -r '$root/empty-success' ]] && grep -Fxq "\$source_file" '$root/empty-success'; then exit 0; fi
|
||||
if [[ -r '$root/delay' ]] && grep -Fxq "\$source_file" '$root/delay'; then sleep 1; fi
|
||||
exec '$(pwd)/scripts/compile_wasm_unit' "\$@"
|
||||
SHIM
|
||||
chmod +x "$root/compile"
|
||||
|
||||
cat >"$site/driver.uce" <<'UCE'
|
||||
CLI(Request& context) {
|
||||
if(context.get["health"] == "1") { print(request_perf()["worker_pid"].to_u64(), "|health"); return; }
|
||||
if(context.get["dynamic"] == "1") { print(component("slow-component.uce")); return; }
|
||||
String target = first(context.get["target"], "slow.uce");
|
||||
print(unit_compile(target) ? "compiled" : "failed");
|
||||
}
|
||||
UCE
|
||||
printf '%s\n' 'CLI(Request& context) { print("entry"); }' >"$site/cold-entry.uce"
|
||||
printf '%s\n' 'CLI(Request& context) { print("slow"); }' >"$site/slow.uce"
|
||||
printf '%s\n' 'CLI(Request& context) { print("silent"); }' >"$site/silent.uce"
|
||||
printf '%s\n' 'CLI(Request& context) { print("empty"); }' >"$site/empty.uce"
|
||||
printf '%s\n' '#load "transitive-child.uce"' 'CLI(Request& context) { print("parent"); }' >"$site/transitive.uce"
|
||||
printf '%s\n' 'String transitive_value() { return("a"); }' >"$site/transitive-child.uce"
|
||||
printf '%s\n' 'COMPONENT(Request& context) { print("component"); }' >"$site/components/slow-component.uce"
|
||||
|
||||
timeout 40s env UCE_PRECOMPILE_FILES_IN="$site" UCE_PRECOMPILE_BIN_DIRECTORY="$work" UCE_PRECOMPILE_JOBS=1 bin/uce_fastcgi.linux.bin --precompile >"$root/precompile.log" 2>&1 || { cat "$root/precompile.log" >&2; exit 1; }
|
||||
cache="$(scripts/unit_cache_directory "$work")$(realpath "$site")"
|
||||
generation=$(scripts/unit_cache_directory "$work")
|
||||
rm -f "$cache/cold-entry.uce."* "$cache/components/slow-component.uce."*
|
||||
|
||||
timeout --signal=TERM --kill-after=5s 90s bin/uce_fastcgi.linux.bin >"$log" 2>&1 &
|
||||
server_pid=$!
|
||||
for _ in $(seq 1 400); do [[ -S "$root/run/cli.sock" ]] && break; sleep 0.02; done
|
||||
[[ -S "$root/run/cli.sock" ]] || { cat "$log" >&2; exit 1; }
|
||||
|
||||
request() {
|
||||
local spec="$1" path="${1%%\?*}"
|
||||
if [[ "$spec" == *\?* ]]; then
|
||||
local query="${spec#*\?}"
|
||||
IFS='&' read -r -a params <<<"$query"
|
||||
timeout 5s scripts/uce-cli --get "$path" "${params[@]}"
|
||||
else
|
||||
timeout 5s scripts/uce-cli "$path"
|
||||
fi
|
||||
}
|
||||
driver=""
|
||||
for _ in $(seq 1 100); do
|
||||
set +e
|
||||
driver=$(request '/driver.uce?health=1' 2>/dev/null)
|
||||
rc=$?
|
||||
set -e
|
||||
(( rc == 0 )) && [[ "$driver" == *'|health' ]] && break
|
||||
sleep 0.05
|
||||
done
|
||||
[[ "$driver" == *'|health' ]] || { echo "driver did not become ready: $driver" >&2; exit 1; }
|
||||
worker_pid=${driver%%|*}
|
||||
[[ "$driver" == "$worker_pid|health" ]]
|
||||
[[ "$(request /driver.uce)" == compiled ]]
|
||||
before=$(sha256sum "$cache/slow.uce.wasm" "$cache/slow.uce.wasm.source-map" "$cache/slow.uce.cpp" "$cache/slow.uce.exports.txt" "$cache/slow.uce.meta.txt")
|
||||
|
||||
assert_timeout() {
|
||||
local url="$1" source="$2" started elapsed output
|
||||
printf '%s\n' "$source" >"$root/hold"
|
||||
rm -f "$root/compiler-pid" "$root/late-descendant"
|
||||
started=$(date +%s%N)
|
||||
set +e
|
||||
output=$(request "$url" 2>&1)
|
||||
set -e
|
||||
elapsed=$(( ($(date +%s%N) - started) / 1000000 ))
|
||||
[[ "$output" == *UCE_INVOCATION_TIMEOUT:* ]] || { echo "$url lacked timeout: $output" >&2; exit 1; }
|
||||
(( elapsed >= 1700 && elapsed <= 3500 )) || { echo "$url took ${elapsed}ms" >&2; exit 1; }
|
||||
[[ "$(request '/driver.uce?health=1')" == "$worker_pid|health" ]]
|
||||
[[ -s "$root/compiler-pid" ]]
|
||||
compiler_pid=$(<"$root/compiler-pid")
|
||||
! kill -0 "$compiler_pid" 2>/dev/null || { echo "compiler $compiler_pid survived" >&2; exit 1; }
|
||||
sleep 0.1
|
||||
[[ ! -e "$root/late-descendant" ]]
|
||||
rm -f "$root/hold"
|
||||
}
|
||||
|
||||
assert_timeout /driver.uce "$site/slow.uce"
|
||||
[[ "$before" == "$(sha256sum "$cache/slow.uce.wasm" "$cache/slow.uce.wasm.source-map" "$cache/slow.uce.cpp" "$cache/slow.uce.exports.txt" "$cache/slow.uce.meta.txt")" ]]
|
||||
[[ ! -e "$cache/slow.uce.compile.txt" && ! -e "$cache/slow.uce.wasm-check.txt" ]]
|
||||
! find "$cache" -name '*.invocation-*' -print -quit | grep -q .
|
||||
[[ "$(request /driver.uce)" == compiled ]]
|
||||
before=$(sha256sum "$cache/slow.uce.wasm" "$cache/slow.uce.wasm.source-map" "$cache/slow.uce.cpp" "$cache/slow.uce.exports.txt" "$cache/slow.uce.meta.txt")
|
||||
|
||||
printf '%s\n' "$site/silent.uce" >"$root/silent-fail"
|
||||
[[ "$(request '/driver.uce?target=silent.uce')" == failed ]]
|
||||
grep -q 'status 23' "$cache/silent.uce.compile.txt"
|
||||
rm -f "$root/silent-fail"
|
||||
printf '%s\n' "$site/empty.uce" >"$root/empty-success"
|
||||
[[ "$(request '/driver.uce?target=empty.uce')" == failed ]]
|
||||
grep -q 'without creating' "$cache/empty.uce.compile.txt"
|
||||
rm -f "$root/empty-success"
|
||||
! find "$cache" -name '*.invocation-*' -print -quit | grep -q .
|
||||
|
||||
printf '%s\n' "$site/slow.uce" >"$root/stage-timeout"
|
||||
assert_timeout /driver.uce "$site/slow.uce"
|
||||
rm -f "$root/stage-timeout"
|
||||
[[ "$before" == "$(sha256sum "$cache/slow.uce.wasm" "$cache/slow.uce.wasm.source-map" "$cache/slow.uce.cpp" "$cache/slow.uce.exports.txt" "$cache/slow.uce.meta.txt")" ]]
|
||||
|
||||
transitive_before=$(sha256sum "$cache/transitive.uce.wasm" "$cache/transitive.uce.wasm.source-map" "$cache/transitive.uce.cpp" "$cache/transitive.uce.exports.txt" "$cache/transitive.uce.meta.txt")
|
||||
printf '%s\n' 'String transitive_value() { return("b"); }' >"$site/transitive-child.uce"
|
||||
assert_timeout '/driver.uce?target=transitive.uce' "$site/transitive-child.uce"
|
||||
[[ "$transitive_before" == "$(sha256sum "$cache/transitive.uce.wasm" "$cache/transitive.uce.wasm.source-map" "$cache/transitive.uce.cpp" "$cache/transitive.uce.exports.txt" "$cache/transitive.uce.meta.txt")" ]]
|
||||
|
||||
assert_timeout '/driver.uce?dynamic=1' "$site/components/slow-component.uce"
|
||||
assert_timeout /cold-entry.uce "$site/cold-entry.uce"
|
||||
|
||||
printf '%s\n' "$site/slow.uce" >"$root/delay"
|
||||
timeout 25s env UCE_PRECOMPILE_FILES_IN="$site" UCE_PRECOMPILE_BIN_DIRECTORY="$work" UCE_PRECOMPILE_JOBS=1 bin/uce_fastcgi.linux.bin --precompile >/dev/null
|
||||
rm -f "$root/delay"
|
||||
|
||||
exec 8>"$generation/known-uce-files.txt.lock"
|
||||
flock 8
|
||||
started=$(date +%s%N)
|
||||
set +e
|
||||
registry_locked=$(request /driver.uce 2>&1)
|
||||
set -e
|
||||
elapsed=$(( ($(date +%s%N) - started) / 1000000 ))
|
||||
flock -u 8
|
||||
[[ "$registry_locked" == *UCE_INVOCATION_TIMEOUT:* && "$elapsed" -le 3500 ]]
|
||||
|
||||
exec 9>"$cache/slow.uce.wasm.lock"
|
||||
flock 9
|
||||
started=$(date +%s%N)
|
||||
set +e
|
||||
locked=$(request /driver.uce 2>&1)
|
||||
set -e
|
||||
elapsed=$(( ($(date +%s%N) - started) / 1000000 ))
|
||||
flock -u 9
|
||||
[[ "$locked" == *UCE_INVOCATION_TIMEOUT:* && "$elapsed" -le 3500 ]]
|
||||
|
||||
echo "wasm compile timeout passed (entry, explicit, dynamic, unit/registry locks, error page, process group, staging, native precompile)"
|
||||
@@ -113,7 +113,7 @@ for unit in hostcall-loop.uce legacy-shell.uce sleep.uce; do
|
||||
expected_ms=5000
|
||||
fi
|
||||
else
|
||||
grep -q 'UCE_INVOCATION_TIMEOUT:' "$body_file" || { echo "$unit lacked invocation-timeout classification" >&2; exit 1; }
|
||||
grep -q 'UCE_INVOCATION_TIMEOUT:' "$body_file" || { echo "$unit lacked invocation-timeout classification: $(cat "$body_file")" >&2; exit 1; }
|
||||
expected_ms=$invocation_ms
|
||||
fi
|
||||
(( elapsed_ms >= expected_ms - 1000 && elapsed_ms <= expected_ms + 3000 )) || { echo "$unit completed in ${elapsed_ms}ms; expected about ${expected_ms}ms" >&2; exit 1; }
|
||||
|
||||
Reference in New Issue
Block a user