Stage ABI-scoped unit generations
This commit is contained in:
parent
810c19b042
commit
1952c7d762
@ -215,7 +215,9 @@ Important settings:
|
||||
- `FCGI_SOCKET_MODE` and `CLI_SOCKET_MODE` are octal permission modes applied after socket bind. Prefer tightening `FCGI_SOCKET_MODE` to `0660` when nginx/Apache can share a trusted group with the UCE worker.
|
||||
- `SITE_DIRECTORY` is the public site tree to scan for `.uce` files. Use `/var/www/html` when the web root is outside the runtime tree; relative paths are resolved from the runtime working directory. Installed regression gate scripts derive their temporary test root from this setting unless `UCE_TEST_SITE_DIRECTORY` is explicitly provided.
|
||||
- `HTTP_DOCUMENT_ROOT` is the root used by the built-in HTTP/WebSocket listener when it resolves upgrade requests. Set it to the same web root as nginx/Apache.
|
||||
- `BIN_DIRECTORY` stores generated C++, wasm artifacts, compile output, and runtime caches.
|
||||
- `BIN_DIRECTORY` stores runtime state plus ABI-scoped unit generations. Unit
|
||||
C++, wasm, serialized modules, source maps, and compile diagnostics live in
|
||||
`units-c<compiler ABI>-w<core ABI>` so an upgrade cannot mix generations.
|
||||
- `TMP_UPLOAD_PATH` and `SESSION_PATH` must be writable by the runtime.
|
||||
- `SESSION_COOKIE_SECURE=1` adds the `Secure` attribute to UCE-managed session cookies and should be used for HTTPS-only deployments. Leave it `0` only for local/plain-HTTP development.
|
||||
- `MYSQL_PERSISTENT_POOL_SIZE` caps credential-keyed connections retained by each Wasm worker. The default `8` is clamped to `64`; set it to `0` to restore request-lifetime connections. Cached sessions are reset before reuse.
|
||||
@ -697,7 +699,14 @@ Common compile footguns:
|
||||
- `WASMTIME_HOME` does not point at a tree with Wasmtime headers and `libwasmtime.so`.
|
||||
- A previous failed compile left stale `.compile.txt`, `.wasm-check.txt`, or partial `.wasm` files under `BIN_DIRECTORY`.
|
||||
|
||||
Failed compile output is persisted under the unit's generated path in `BIN_DIRECTORY` and may be reused until the source or compiler inputs change. First fix the source/toolchain issue and reload the page. If the cache itself is suspect, stop UCE, move only the affected unit artifact files or directory aside, and restart so the runtime recompiles from source. Avoid deleting the whole `BIN_DIRECTORY` unless you intentionally want a full rebuild.
|
||||
Failed compile output is persisted under the unit's ABI-generation path in
|
||||
`BIN_DIRECTORY` and may be reused until the source or compiler inputs change.
|
||||
The managed `restart` command first runs the new binary's `--precompile` mode as
|
||||
the configured service user while the old service remains live. It restarts
|
||||
systemd only after every scanned unit compiles and serializes successfully.
|
||||
First fix source/toolchain failures and retry; do not remove the prior generation,
|
||||
which remains the rollback path. Avoid deleting the whole `BIN_DIRECTORY` unless
|
||||
you intentionally want a full rebuild and have accepted losing rollback artifacts.
|
||||
|
||||
### CLI commands fail
|
||||
|
||||
|
||||
@ -47,7 +47,9 @@ Treat WASI SDK upgrades like runtime dependency upgrades:
|
||||
2. Record the new release and checksum here.
|
||||
3. Rebuild `bin/wasm/core.wasm` with `scripts/build_core_wasm.sh`.
|
||||
4. Rebuild the native runtime with `scripts/build_linux.sh`.
|
||||
5. Clear or invalidate stale unit wasm artifacts by bumping `UCE_UNIT_ABI_VERSION` when required, or by removing affected generated artifacts under `BIN_DIRECTORY`.
|
||||
5. Bump the compiler or core ABI constant in `src/wasm/abi.h` when required.
|
||||
The managed restart precompiles the resulting isolated unit generation before
|
||||
switching workers; do not overwrite or delete the previous generation.
|
||||
6. Run the full CLI suite including wasm kill tests:
|
||||
|
||||
```bash
|
||||
|
||||
@ -232,6 +232,14 @@ 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.
|
||||
|
||||
Unit artifacts live beneath an ABI-generation directory such as
|
||||
`BIN_DIRECTORY/units-c12-w7`: `c12` is the compiler/unit-metadata ABI and `w7`
|
||||
is the runtime core ABI. Old and new service binaries therefore never publish
|
||||
or read the same unit path during an ABI transition. The managed restart builds
|
||||
and serializes the complete next generation before stopping the old service;
|
||||
failure aborts the switch and leaves the running generation intact. Old
|
||||
generation directories are retained as an explicit rollback path.
|
||||
|
||||
The proactive compiler and request workers coordinate through per-unit file
|
||||
locks and a lock-protected demand-priority queue under `BIN_DIRECTORY`. A small
|
||||
priority-only compiler process drains that queue independently of the full-site
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
cd ..
|
||||
|
||||
@ -6,11 +7,14 @@ BUILDMODE=${2:-"debug"}
|
||||
OPT_FLAG="O0"
|
||||
GF="uce_fastcgi"
|
||||
|
||||
mkdir bin > /dev/null 2>&1
|
||||
mkdir bin/tmp > /dev/null 2>&1
|
||||
mkdir bin/assets > /dev/null 2>&1
|
||||
mkdir bin/wasm > /dev/null 2>&1
|
||||
mkdir work > /dev/null 2>&1
|
||||
mkdir -p bin/tmp bin/assets bin/wasm work
|
||||
exec 9>bin/.build.lock
|
||||
flock 9
|
||||
build_tmp_files=()
|
||||
cleanup_build_tmp() {
|
||||
((${#build_tmp_files[@]} == 0)) || rm -f "${build_tmp_files[@]}"
|
||||
}
|
||||
trap cleanup_build_tmp EXIT
|
||||
|
||||
COMPILER="clang++"
|
||||
# -rdynamic is a link-time flag; the -c compiles below do not need it.
|
||||
@ -52,13 +56,16 @@ fi
|
||||
# SQLite: vendored C, depends only on its own source (not our headers).
|
||||
if needs_rebuild bin/sqlite3.o src/3rdparty/sqlite/sqlite3.c src/3rdparty/sqlite/sqlite3.h; then
|
||||
echo "Compiling SQLite..."
|
||||
tmp="bin/sqlite3.o.tmp.$$"
|
||||
build_tmp_files+=("$tmp")
|
||||
clang -g -O2 -fPIC \
|
||||
-DSQLITE_THREADSAFE=1 \
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION=1 \
|
||||
-DSQLITE_DQS=0 \
|
||||
-DSQLITE_DEFAULT_FOREIGN_KEYS=1 \
|
||||
-DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
|
||||
-c src/3rdparty/sqlite/sqlite3.c -o bin/sqlite3.o 2>&1 || exit 1
|
||||
-c src/3rdparty/sqlite/sqlite3.c -o "$tmp" 2>&1
|
||||
mv "$tmp" bin/sqlite3.o
|
||||
else
|
||||
echo "Reusing bin/sqlite3.o"
|
||||
fi
|
||||
@ -67,7 +74,10 @@ fi
|
||||
# declarations (not the lib .cpp — those are compiled into main.o).
|
||||
if needs_rebuild bin/wasm.o src/wasm src/lib/*.h; then
|
||||
echo "Compiling wasm backend..."
|
||||
time -p $COMPILER -c src/wasm/wasm_module.cpp $SRCFLAGS $FLAGS $WASM_FLAGS -o bin/wasm.o 2>&1 || exit 1
|
||||
tmp="bin/wasm.o.tmp.$$"
|
||||
build_tmp_files+=("$tmp")
|
||||
time -p $COMPILER -c src/wasm/wasm_module.cpp $SRCFLAGS $FLAGS $WASM_FLAGS -o "$tmp" 2>&1
|
||||
mv "$tmp" bin/wasm.o
|
||||
else
|
||||
echo "Reusing bin/wasm.o"
|
||||
fi
|
||||
@ -77,18 +87,19 @@ fi
|
||||
# (its only view of the wasm object) — but not the wasm .cpp sources.
|
||||
if needs_rebuild bin/main.o src/linux_fastcgi.cpp src/lib src/fastcgi src/wasm/backend.h; then
|
||||
echo "Compiling main..."
|
||||
time -p $COMPILER -c src/linux_fastcgi.cpp $SRCFLAGS $FLAGS -o bin/main.o 2>&1 || exit 1
|
||||
tmp="bin/main.o.tmp.$$"
|
||||
build_tmp_files+=("$tmp")
|
||||
time -p $COMPILER -c src/linux_fastcgi.cpp $SRCFLAGS $FLAGS -o "$tmp" 2>&1
|
||||
mv "$tmp" bin/main.o
|
||||
else
|
||||
echo "Reusing bin/main.o"
|
||||
fi
|
||||
|
||||
echo "Linking..."
|
||||
$COMPILER -rdynamic bin/main.o bin/wasm.o bin/sqlite3.o $FLAGS $LIBS -o bin/$GF.linux.bin 2>&1
|
||||
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
ls -lh bin/ | grep $GF
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
binary_tmp="bin/$GF.linux.bin.tmp.$$"
|
||||
build_tmp_files+=("$binary_tmp")
|
||||
$COMPILER -rdynamic bin/main.o bin/wasm.o bin/sqlite3.o $FLAGS $LIBS -o "$binary_tmp" 2>&1
|
||||
test -s "$binary_tmp"
|
||||
chmod 0755 "$binary_tmp"
|
||||
mv "$binary_tmp" "bin/$GF.linux.bin"
|
||||
ls -lh "bin/$GF.linux.bin"
|
||||
|
||||
@ -160,7 +160,13 @@ def defined_symbols(path: Path, llvm_nm: str) -> list[str]:
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("wasm", type=Path)
|
||||
ap.add_argument("--abi-version", default="7")
|
||||
default_abi = "7"
|
||||
abi_header = Path(__file__).resolve().parents[1] / "src" / "wasm" / "abi.h"
|
||||
for line in abi_header.read_text().splitlines():
|
||||
if line.startswith("#define UCE_WASM_CORE_ABI_VERSION "):
|
||||
default_abi = line.rsplit(" ", 1)[1]
|
||||
break
|
||||
ap.add_argument("--abi-version", default=default_abi)
|
||||
ap.add_argument("--llvm-nm", default=None)
|
||||
ap.add_argument("--verbose", action="store_true")
|
||||
args = ap.parse_args()
|
||||
|
||||
@ -11,7 +11,7 @@ PP_FN="$4"
|
||||
WASM_FN="$5"
|
||||
|
||||
SDK=${WASI_SDK:-/opt/wasi-sdk}
|
||||
ABI_VERSION=${UCE_UNIT_ABI_VERSION:-7}
|
||||
ABI_VERSION=${UCE_UNIT_ABI_VERSION:-$(awk '/^#define UCE_WASM_CORE_ABI_VERSION / {print $3; exit}' src/wasm/abi.h)}
|
||||
ROOT=$(pwd)
|
||||
OBJ_FN="$DEST_DIR/$PP_FN.wasm.o"
|
||||
ABI_TMP="$DEST_DIR/$PP_FN.uce-abi.txt"
|
||||
|
||||
@ -67,6 +67,7 @@ if [[ "$action" == "run" ]]; then
|
||||
curl -sS --max-time "$curl_timeout" --fail-with-body --unix-socket "$socket_path" "${base_url}&group=${group}"
|
||||
done
|
||||
scripts/test_dependency_invalidation.sh
|
||||
scripts/test_abi_generation_rollout.sh
|
||||
scripts/test_cold_component_deadline.sh
|
||||
scripts/test_nested_component_props.sh
|
||||
scripts/test_component_once_prefetch.sh
|
||||
|
||||
@ -56,6 +56,15 @@ case "$action" in
|
||||
if [[ "$REPO_ROOT" != "/usr/lib/uce" && "$REPO_ROOT" != "/opt/uce" ]]; then
|
||||
"$REPO_ROOT/scripts/build_linux.sh"
|
||||
fi
|
||||
(
|
||||
cd "$REPO_ROOT"
|
||||
service_user=$(systemctl show "$UNIT_NAME" -p User --value)
|
||||
if [[ -n "$service_user" && "$(id -u)" == "0" ]]; then
|
||||
runuser -u "$service_user" -- "$REPO_ROOT/bin/uce_fastcgi.linux.bin" --precompile
|
||||
else
|
||||
"$REPO_ROOT/bin/uce_fastcgi.linux.bin" --precompile
|
||||
fi
|
||||
)
|
||||
systemctl restart "$UNIT_NAME"
|
||||
;;
|
||||
start|stop|status)
|
||||
|
||||
@ -10,7 +10,7 @@ if [[ -r /etc/uce/settings.cfg ]]; then
|
||||
fi
|
||||
|
||||
for ((attempt = 0; attempt < 200; attempt++)); do
|
||||
if [[ -S "$socket_path" ]]; then
|
||||
if [[ -S "$socket_path" ]] && [[ "$(curl -sS --max-time 0.2 --unix-socket "$socket_path" http://localhost/ping 2>/dev/null || true)" == "uce-cli: ok" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 0.05
|
||||
|
||||
101
scripts/test_abi_generation_rollout.sh
Executable file
101
scripts/test_abi_generation_rollout.sh
Executable file
@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
test_name="abi-generation-test-$$"
|
||||
site_directory="${UCE_TEST_SITE_DIRECTORY:-site}"
|
||||
if [[ -z "${UCE_TEST_SITE_DIRECTORY:-}" && -r /etc/uce/settings.cfg ]]; then
|
||||
configured_site_directory=$(awk -F= '/^[[:space:]]*SITE_DIRECTORY[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2; exit}' /etc/uce/settings.cfg)
|
||||
[[ -z "${configured_site_directory:-}" ]] || site_directory="$configured_site_directory"
|
||||
fi
|
||||
source_dir="$site_directory/$test_name"
|
||||
http_host="${UCE_TEST_HTTP_HOST:-uce.openfu.com}"
|
||||
body_file="/tmp/uce-abi-generation-body-$$"
|
||||
ready_file="/tmp/uce-abi-generation-ready-$$"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$source_dir"
|
||||
rm -f "$body_file" "$ready_file"
|
||||
[[ -z "${artifact_dir:-}" ]] || rm -rf "$artifact_dir"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
mkdir -p "$source_dir"
|
||||
|
||||
printf '%s\n' \
|
||||
'RENDER(Request& context) { print("entry-current"); }' \
|
||||
'CLI(Request& context) {' \
|
||||
' if(context.get["artifact"] == "entry") print(unit_info(context.params["SCRIPT_FILENAME"])["wasm_name"].to_string());' \
|
||||
' else print("entry-current");' \
|
||||
'}' >"$source_dir/entry.uce"
|
||||
printf '%s\n' \
|
||||
'COMPONENT(Request& context) { print("component-current"); }' >"$source_dir/child.uce"
|
||||
printf '%s\n' \
|
||||
'RENDER(Request& context) { DValue props; print(component("child", props, context)); }' \
|
||||
'CLI(Request& context) {' \
|
||||
' DValue props; component("child", props, context);' \
|
||||
' print(unit_info("child.uce")["wasm_name"].to_string());' \
|
||||
'}' >"$source_dir/component-parent.uce"
|
||||
|
||||
entry_url="http://127.0.0.1/$test_name/entry.uce"
|
||||
component_url="http://127.0.0.1/$test_name/component-parent.uce"
|
||||
[[ "$(curl -fsS -H "Host: $http_host" "$entry_url")" == "entry-current" ]]
|
||||
entry_wasm=$(scripts/uce-cli --get "/$test_name/entry.uce" artifact=entry)
|
||||
component_wasm=$(scripts/uce-cli "/$test_name/component-parent.uce")
|
||||
artifact_dir=$(dirname "$entry_wasm")
|
||||
generation_name=$(basename "$(scripts/unit_cache_directory /tmp/uce-generation-probe)")
|
||||
if [[ "$entry_wasm" != *"/$generation_name/"* || "$component_wasm" != *"/$generation_name/"* ]]; then
|
||||
echo "unit artifacts are not isolated by compiler/core ABI generation: $entry_wasm $component_wasm" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
publish_incompatible_artifact_while_locked() {
|
||||
local source_file="$1"
|
||||
local wasm_file="$2"
|
||||
local marker="$3"
|
||||
local destination
|
||||
destination=$(dirname "$wasm_file")
|
||||
(
|
||||
exec 8>"$wasm_file.lock"
|
||||
flock 8
|
||||
UCE_UNIT_ABI_VERSION=6 scripts/compile_wasm_unit \
|
||||
"$(dirname "$source_file")" "$destination" "$source_file" \
|
||||
"$(basename "$source_file").cpp" "$(basename "$wasm_file")"
|
||||
sed -i \
|
||||
-e 's/^unit_abi_version=.*/unit_abi_version=0/' \
|
||||
-e 's/^wasm_core_abi_version=.*/wasm_core_abi_version=0/' \
|
||||
"${wasm_file%.wasm}.meta.txt"
|
||||
rm -f "${wasm_file%.wasm}.cwasm"
|
||||
printf '%s\n' "$marker" >"$ready_file"
|
||||
sleep 2
|
||||
) &
|
||||
lock_pid=$!
|
||||
deadline=$((SECONDS + 20))
|
||||
while [[ ! -e "$ready_file" && $SECONDS -lt $deadline ]]; do sleep 0.05; done
|
||||
if [[ ! -e "$ready_file" || "$(cat "$ready_file")" != "$marker" ]]; then
|
||||
echo "incompatible artifact was not published under lock: $marker" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
publish_incompatible_artifact_while_locked "$source_dir/entry.uce" "$entry_wasm" entry
|
||||
started_at=$(date +%s%N)
|
||||
entry_status=$(curl -sS --max-time 30 -o "$body_file" -w '%{http_code}' -H "Host: $http_host" "$entry_url")
|
||||
entry_elapsed_ms=$(( ($(date +%s%N) - started_at) / 1000000 ))
|
||||
wait "$lock_pid"
|
||||
if [[ "$entry_status" != "200" || "$(cat "$body_file")" != "entry-current" || $entry_elapsed_ms -lt 1500 ]]; then
|
||||
echo "ABI-incompatible entry artifact was served instead of joining its rebuild: status=$entry_status elapsed=${entry_elapsed_ms}ms body=$(cat "$body_file")" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f "$ready_file"
|
||||
publish_incompatible_artifact_while_locked "$source_dir/child.uce" "$component_wasm" component
|
||||
started_at=$(date +%s%N)
|
||||
component_status=$(curl -sS --max-time 30 -o "$body_file" -w '%{http_code}' -H "Host: $http_host" "$component_url")
|
||||
component_elapsed_ms=$(( ($(date +%s%N) - started_at) / 1000000 ))
|
||||
wait "$lock_pid"
|
||||
if [[ "$component_status" != "200" || "$(cat "$body_file")" != "component-current" || $component_elapsed_ms -lt 1500 ]]; then
|
||||
echo "ABI-incompatible component artifact was served instead of joining its rebuild: status=$component_status elapsed=${component_elapsed_ms}ms body=$(cat "$body_file")" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "ABI generation rollout passed"
|
||||
@ -26,7 +26,7 @@ cleanup() {
|
||||
}
|
||||
trap cleanup EXIT
|
||||
mkdir -p "$source_dir"
|
||||
cache_dir="$bin_directory$(realpath "$source_dir")"
|
||||
cache_dir="$(scripts/unit_cache_directory "$bin_directory")$(realpath "$source_dir")"
|
||||
|
||||
printf '%s\n' \
|
||||
'CLI(Request& context) { DValue props; print(component("child", props, context)); }' >"$source_dir/parent.uce"
|
||||
|
||||
@ -40,7 +40,7 @@ cleanup() {
|
||||
}
|
||||
trap cleanup EXIT
|
||||
mkdir -p "$source_dir"
|
||||
cache_dir="$bin_directory$(realpath "$source_dir")"
|
||||
cache_dir="$(scripts/unit_cache_directory "$bin_directory")$(realpath "$source_dir")"
|
||||
|
||||
printf '%s\n' \
|
||||
'#ifndef UCE_DEPENDENCY_CACHE_CHILD' \
|
||||
|
||||
@ -26,7 +26,7 @@ cleanup() {
|
||||
}
|
||||
trap cleanup EXIT
|
||||
mkdir -p "$source_dir"
|
||||
cache_dir="$bin_directory$(realpath "$source_dir")"
|
||||
cache_dir="$(scripts/unit_cache_directory "$bin_directory")$(realpath "$source_dir")"
|
||||
|
||||
printf '%s\n' 'CLI(Request& context) { deliberate_log_timeliness_compile_failure }' >"$source_dir/probe.uce"
|
||||
rm -rf "$cache_dir"
|
||||
|
||||
@ -38,7 +38,7 @@ cleanup() {
|
||||
}
|
||||
trap cleanup EXIT
|
||||
mkdir -p "$source_dir"
|
||||
cache_dir="$bin_directory$(realpath "$source_dir")"
|
||||
cache_dir="$(scripts/unit_cache_directory "$bin_directory")$(realpath "$source_dir")"
|
||||
mariadb -e "DROP USER IF EXISTS '$test_user'@'127.0.0.1'; CREATE USER '$test_user'@'127.0.0.1' IDENTIFIED BY '$test_password'"
|
||||
test_user_created=true
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ cleanup() {
|
||||
}
|
||||
trap cleanup EXIT
|
||||
mkdir -p "$source_dir"
|
||||
cache_dir="$bin_directory$(realpath "$source_dir")"
|
||||
cache_dir="$(scripts/unit_cache_directory "$bin_directory")$(realpath "$source_dir")"
|
||||
mariadb -e "DROP DATABASE IF EXISTS \`$test_database\`; DROP DATABASE IF EXISTS \`$test_database_other\`; DROP USER IF EXISTS '$test_user'@'127.0.0.1'; CREATE DATABASE \`$test_database\`; CREATE DATABASE \`$test_database_other\`; CREATE USER '$test_user'@'127.0.0.1' IDENTIFIED BY '$test_password'; GRANT ALL ON \`$test_database\`.* TO '$test_user'@'127.0.0.1'; GRANT ALL ON \`$test_database_other\`.* TO '$test_user'@'127.0.0.1'; CREATE TABLE \`$test_database\`.pool_identity (label VARCHAR(16) NOT NULL); INSERT INTO \`$test_database\`.pool_identity VALUES ('primary'); CREATE TABLE \`$test_database_other\`.pool_identity (label VARCHAR(16) NOT NULL); INSERT INTO \`$test_database_other\`.pool_identity VALUES ('other')"
|
||||
|
||||
printf '%s\n' \
|
||||
|
||||
@ -26,7 +26,7 @@ cleanup() {
|
||||
}
|
||||
trap cleanup EXIT
|
||||
mkdir -p "$source_dir"
|
||||
cache_dir="$bin_directory$(realpath "$source_dir")"
|
||||
cache_dir="$(scripts/unit_cache_directory "$bin_directory")$(realpath "$source_dir")"
|
||||
|
||||
printf '%s\n' \
|
||||
'CLI(Request& context) {' \
|
||||
|
||||
@ -27,7 +27,7 @@ cleanup() {
|
||||
trap cleanup EXIT
|
||||
mkdir -p "$source_dir"
|
||||
absolute_source_dir=$(realpath "$source_dir")
|
||||
artifact_dir="$bin_directory$absolute_source_dir"
|
||||
artifact_dir="$(scripts/unit_cache_directory "$bin_directory")$absolute_source_dir"
|
||||
|
||||
printf '%s\n' \
|
||||
'String visibility_used() { return("private-used"); }' \
|
||||
|
||||
@ -22,7 +22,7 @@ cleanup() {
|
||||
trap cleanup EXIT
|
||||
mkdir -p "$source_dir"
|
||||
absolute_source_dir=$(realpath "$source_dir")
|
||||
artifact_dir="$bin_directory$absolute_source_dir"
|
||||
artifact_dir="$(scripts/unit_cache_directory "$bin_directory")$absolute_source_dir"
|
||||
printf '%s\n' 'CLI(Request& context) { __builtin_trap(); }' >"$source_dir/entry.uce"
|
||||
|
||||
set +e
|
||||
|
||||
8
scripts/unit_cache_directory
Executable file
8
scripts/unit_cache_directory
Executable file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
base="${1:-/tmp/uce/work}"
|
||||
compiler_abi=$(awk '/^#define UCE_COMPILER_UNIT_ABI_VERSION / {print $3; exit}' src/wasm/abi.h)
|
||||
core_abi=$(awk '/^#define UCE_WASM_CORE_ABI_VERSION / {print $3; exit}' src/wasm/abi.h)
|
||||
printf '%s/units-c%s-w%s\n' "${base%/}" "$compiler_abi" "$core_abi"
|
||||
@ -1,6 +1,7 @@
|
||||
#include "compiler.h"
|
||||
#include "compiler-parser.h"
|
||||
#include "hash.h"
|
||||
#include "../wasm/abi.h"
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
@ -16,7 +17,7 @@
|
||||
|
||||
namespace {
|
||||
|
||||
const u64 UCE_UNIT_ABI_VERSION = 11;
|
||||
const u64 UCE_UNIT_ABI_VERSION = UCE_COMPILER_UNIT_ABI_VERSION;
|
||||
|
||||
struct SharedUnitFilesystemState
|
||||
{
|
||||
@ -35,6 +36,8 @@ struct SharedUnitFilesystemState
|
||||
time_t required_time = 0;
|
||||
u64 metadata_abi_version = 0;
|
||||
u64 runtime_abi_version = UCE_UNIT_ABI_VERSION;
|
||||
u64 metadata_wasm_core_abi_version = 0;
|
||||
u64 runtime_wasm_core_abi_version = UCE_WASM_CORE_ABI_VERSION;
|
||||
String metadata_content;
|
||||
String compile_output_content;
|
||||
String metadata_build_token;
|
||||
@ -262,7 +265,8 @@ String compiler_unit_input_signature(Request* context, SharedUnit* su, bool allo
|
||||
return(
|
||||
compiler_unit_source_signature(su->file_name, allow_recent_source_stat) + ":" +
|
||||
gen_sha1(file_get_contents(setup_template)) + ":" +
|
||||
std::to_string(UCE_UNIT_ABI_VERSION)
|
||||
std::to_string(UCE_UNIT_ABI_VERSION) + ":" +
|
||||
std::to_string(UCE_WASM_CORE_ABI_VERSION)
|
||||
);
|
||||
}
|
||||
|
||||
@ -281,6 +285,7 @@ String compiler_unit_metadata_text(Request* context, SharedUnit* su, String inpu
|
||||
return(
|
||||
"format=uce-unit-metadata-v1\n"
|
||||
"unit_abi_version=" + std::to_string(UCE_UNIT_ABI_VERSION) + "\n"
|
||||
"wasm_core_abi_version=" + std::to_string(UCE_WASM_CORE_ABI_VERSION) + "\n"
|
||||
"source_path=" + su->file_name + "\n"
|
||||
"input_signature=" + input_signature + "\n"
|
||||
"build_token=" + compiler_unit_build_token() + "\n"
|
||||
@ -387,7 +392,7 @@ time_t compiler_runtime_abi_time(Request* context)
|
||||
|
||||
String compiler_registry_file_name(Request* context)
|
||||
{
|
||||
return(context->server->config["BIN_DIRECTORY"] + "/known-uce-files.txt");
|
||||
return(compiler_unit_bin_directory(context) + "/known-uce-files.txt");
|
||||
}
|
||||
|
||||
String compiler_registry_lock_file_name(Request* context)
|
||||
@ -397,12 +402,12 @@ String compiler_registry_lock_file_name(Request* context)
|
||||
|
||||
String compiler_priority_file_name(Request* context)
|
||||
{
|
||||
return(context->server->config["BIN_DIRECTORY"] + "/proactive-priority.txt");
|
||||
return(compiler_unit_bin_directory(context) + "/proactive-priority.txt");
|
||||
}
|
||||
|
||||
String compiler_source_generation_file_name(Request* context)
|
||||
{
|
||||
return(context->server->config["BIN_DIRECTORY"] + "/source-generation.txt");
|
||||
return(compiler_unit_bin_directory(context) + "/source-generation.txt");
|
||||
}
|
||||
|
||||
int compiler_open_lock_file(String file_name, String purpose, bool nonblocking = false)
|
||||
@ -553,6 +558,9 @@ SharedUnitFilesystemState inspect_shared_unit_filesystem(Request* context, Share
|
||||
auto input_it = metadata.find("input_signature");
|
||||
if(input_it != metadata.end())
|
||||
state.metadata_input_signature = input_it->second;
|
||||
auto core_abi_it = metadata.find("wasm_core_abi_version");
|
||||
if(core_abi_it != metadata.end() && compiler_is_u64_string(core_abi_it->second))
|
||||
state.metadata_wasm_core_abi_version = (u64)atoll(core_abi_it->second.c_str());
|
||||
auto source_it = metadata.find("source_path");
|
||||
if(source_it != metadata.end())
|
||||
state.metadata_source_path = source_it->second;
|
||||
@ -562,7 +570,11 @@ SharedUnitFilesystemState inspect_shared_unit_filesystem(Request* context, Share
|
||||
}
|
||||
if(state.compile_output_exists)
|
||||
state.compile_output_content = file_get_contents(su->compile_output_file_name);
|
||||
state.abi_compatible = (state.metadata_parsed && state.metadata_abi_version == state.runtime_abi_version);
|
||||
state.abi_compatible = (
|
||||
state.metadata_parsed &&
|
||||
state.metadata_abi_version == state.runtime_abi_version &&
|
||||
state.metadata_wasm_core_abi_version == state.runtime_wasm_core_abi_version
|
||||
);
|
||||
state.input_signature_matches = (
|
||||
state.metadata_parsed &&
|
||||
state.metadata_input_signature != "" &&
|
||||
@ -791,7 +803,23 @@ String compiler_generated_cpp_path(Request* context, String source_file)
|
||||
{
|
||||
if(!context || !context->server || source_file == "")
|
||||
return("");
|
||||
return(context->server->config["BIN_DIRECTORY"] + dirname(source_file) + "/" + basename(source_file) + ".cpp");
|
||||
return(compiler_unit_bin_directory(context) + dirname(source_file) + "/" + basename(source_file) + ".cpp");
|
||||
}
|
||||
|
||||
String compiler_unit_bin_directory(Request* context)
|
||||
{
|
||||
if(!context || !context->server)
|
||||
return("");
|
||||
return(path_join(
|
||||
context->server->config["BIN_DIRECTORY"],
|
||||
"units-c" + std::to_string(UCE_UNIT_ABI_VERSION) +
|
||||
"-w" + std::to_string(UCE_WASM_CORE_ABI_VERSION)
|
||||
));
|
||||
}
|
||||
|
||||
String compiler_unit_wasm_path(Request* context, String source_file)
|
||||
{
|
||||
return(compiler_unit_bin_directory(context) + source_file + ".wasm");
|
||||
}
|
||||
|
||||
String compiler_generated_cpp_path(SharedUnit* su)
|
||||
@ -809,8 +837,8 @@ void setup_unit_paths(Request* context, SharedUnit* su, String file_name)
|
||||
return;
|
||||
|
||||
su->src_path = dirname(file_name);
|
||||
su->bin_path = context->server->config["BIN_DIRECTORY"] + su->src_path;
|
||||
su->pre_path = context->server->config["BIN_DIRECTORY"] + su->src_path;
|
||||
su->bin_path = compiler_unit_bin_directory(context) + su->src_path;
|
||||
su->pre_path = compiler_unit_bin_directory(context) + su->src_path;
|
||||
|
||||
su->src_file_name = basename(file_name);
|
||||
su->wasm_file_name = su->src_file_name + ".wasm";
|
||||
@ -1023,7 +1051,7 @@ SharedUnit* compiler_get_shared_unit_internal(Request* context, String file_name
|
||||
SharedUnit* su = new SharedUnit();
|
||||
setup_unit_paths(context, su, file_name);
|
||||
|
||||
bool can_serve_stale = !force_recompile && compiler_request_can_serve_stale_artifact(context);
|
||||
bool can_serve_stale = !force_recompile && compiler_unit_can_serve_stale_artifact(context, file_name);
|
||||
int fdlock = compiler_open_lock_file(su->wasm_name + ".lock", "shared-unit:" + file_name, can_serve_stale);
|
||||
if(fdlock == -2 && file_exists(su->wasm_name))
|
||||
{
|
||||
@ -1178,6 +1206,26 @@ bool compiler_request_can_serve_stale_artifact(Request* context)
|
||||
float_val(context->server->config["PROACTIVE_COMPILE_CHECK_INTERVAL"]) > 0);
|
||||
}
|
||||
|
||||
bool compiler_unit_can_serve_stale_artifact(Request* context, String file_name)
|
||||
{
|
||||
if(!compiler_request_can_serve_stale_artifact(context))
|
||||
return(false);
|
||||
file_name = compiler_normalize_unit_path(context, file_name);
|
||||
if(file_name == "")
|
||||
return(false);
|
||||
SharedUnit su;
|
||||
setup_unit_paths(context, &su, file_name);
|
||||
auto state = inspect_shared_unit_filesystem(context, &su, true);
|
||||
return(
|
||||
state.source_exists &&
|
||||
state.compiled_time != 0 &&
|
||||
state.metadata_exists &&
|
||||
state.metadata_parsed &&
|
||||
state.abi_compatible &&
|
||||
state.metadata_source_path == file_name
|
||||
);
|
||||
}
|
||||
|
||||
void compiler_prioritize_unit(Request* context, String file_name)
|
||||
{
|
||||
if(!compiler_request_can_serve_stale_artifact(context))
|
||||
@ -1493,6 +1541,8 @@ DValue unit_info(String path)
|
||||
info["required_mtime"] = (f64)fs_state.required_time;
|
||||
info["runtime_abi_version"] = (f64)fs_state.runtime_abi_version;
|
||||
info["metadata_abi_version"] = (f64)fs_state.metadata_abi_version;
|
||||
info["runtime_wasm_core_abi_version"] = (f64)fs_state.runtime_wasm_core_abi_version;
|
||||
info["metadata_wasm_core_abi_version"] = (f64)fs_state.metadata_wasm_core_abi_version;
|
||||
info["current_input_signature"] = fs_state.current_input_signature;
|
||||
info["metadata_input_signature"] = fs_state.metadata_input_signature;
|
||||
info["metadata_build_token"] = fs_state.metadata_build_token;
|
||||
|
||||
@ -18,6 +18,10 @@
|
||||
String preprocess_shared_unit(Request* context, SharedUnit* su);
|
||||
String compiler_generated_cpp_path(Request* context, String source_file);
|
||||
String compiler_generated_cpp_path(SharedUnit* su);
|
||||
#ifndef __UCE_WASM_UNIT__
|
||||
String compiler_unit_bin_directory(Request* context);
|
||||
String compiler_unit_wasm_path(Request* context, String source_file);
|
||||
#endif
|
||||
void setup_unit_paths(Request* context, SharedUnit* su, String file_name);
|
||||
void compile_shared_unit(Request* context, SharedUnit* su);
|
||||
SharedUnit* get_shared_unit(Request* context, String file_name);
|
||||
@ -25,6 +29,7 @@ String compiler_error_page_unit(Request* context, String config_key);
|
||||
bool compiler_unit_compile_pending(Request* context, String file_name);
|
||||
bool compiler_unit_compile_in_progress(Request* context, String file_name);
|
||||
bool compiler_request_can_serve_stale_artifact(Request* context);
|
||||
bool compiler_unit_can_serve_stale_artifact(Request* context, String file_name);
|
||||
void compiler_prioritize_unit(Request* context, String file_name);
|
||||
StringList compiler_take_priority_units(Request* context);
|
||||
String compiler_source_generation(Request* context);
|
||||
|
||||
@ -1221,7 +1221,7 @@ void proactive_compile_queue_push(StringList& queue, String file_name)
|
||||
bool proactive_compile_unit(Request& context, String file_name, bool& source_missing)
|
||||
{
|
||||
bool failed = false;
|
||||
String wasm_path = server_state.config["BIN_DIRECTORY"] + file_name + ".wasm";
|
||||
String wasm_path = compiler_unit_wasm_path(&context, file_name);
|
||||
if(compiler_unit_needs_recompile(&context, file_name, &source_missing))
|
||||
{
|
||||
printf("(i) proactive compile %s\n", file_name.c_str());
|
||||
@ -1582,6 +1582,36 @@ void init_base_process()
|
||||
srand(time());
|
||||
}
|
||||
|
||||
int precompile_unit_generation()
|
||||
{
|
||||
f64 started_at = time_precise();
|
||||
server_state.config = make_server_settings();
|
||||
server_state.config["COMPILER_SYS_PATH"] = cwd_get();
|
||||
Request background_context;
|
||||
background_context.server = &server_state;
|
||||
mkdir(server_state.config["BIN_DIRECTORY"]);
|
||||
mkdir(compiler_unit_bin_directory(&background_context));
|
||||
|
||||
set_active_request(background_context);
|
||||
auto files = compiler_scan_site_units(&background_context);
|
||||
compiler_set_known_units(&background_context, files);
|
||||
u64 failed = 0;
|
||||
u64 compiled = 0;
|
||||
for(auto& file_name : files)
|
||||
{
|
||||
bool source_missing = false;
|
||||
if(compiler_unit_needs_recompile(&background_context, file_name, &source_missing))
|
||||
compiled++;
|
||||
if(proactive_compile_unit(background_context, file_name, source_missing))
|
||||
failed++;
|
||||
}
|
||||
printf("Precompiled unit generation %s: %zu units, %llu compiled, %llu failed in %.3f s\n",
|
||||
compiler_unit_bin_directory(&background_context).c_str(), files.size(),
|
||||
(unsigned long long)compiled, (unsigned long long)failed,
|
||||
time_precise() - started_at);
|
||||
return(failed == 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// systemd connects stdout to a pipe, which otherwise block-buffers child
|
||||
@ -1592,6 +1622,8 @@ int main(int argc, char** argv)
|
||||
// after a fault does not allocate.
|
||||
backtrace(request_fault_frames, 4);
|
||||
process_start_directory();
|
||||
if(argc == 2 && String(argv[1]) == "--precompile")
|
||||
return(precompile_unit_generation());
|
||||
|
||||
init_base_process();
|
||||
ensure_proactive_compiler();
|
||||
|
||||
4
src/wasm/abi.h
Normal file
4
src/wasm/abi.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#define UCE_WASM_CORE_ABI_VERSION 7
|
||||
#define UCE_COMPILER_UNIT_ABI_VERSION 12
|
||||
@ -42,7 +42,7 @@ static String wasm_backend_ensure_started(Request* context)
|
||||
wc.core_wasm_path = first(cfg["WASM_CORE_PATH"],
|
||||
path_join(cfg["COMPILER_SYS_PATH"], "bin/wasm/core.wasm"));
|
||||
wc.site_root = path_join(cfg["COMPILER_SYS_PATH"], cfg["SITE_DIRECTORY"]);
|
||||
wc.cache_root = cfg["BIN_DIRECTORY"];
|
||||
wc.cache_root = compiler_unit_bin_directory(context);
|
||||
// write membrane allowlist: the site tree plus the runtime scratch dirs
|
||||
// pages legitimately write to (matches native reachable write targets).
|
||||
wc.write_roots = { wc.site_root, "/tmp" };
|
||||
@ -114,7 +114,7 @@ static bool wasm_artifact_exists(Request* context, const String& entry_unit)
|
||||
{
|
||||
if(entry_unit == "")
|
||||
return(false);
|
||||
String wasm_path = context->server->config["BIN_DIRECTORY"] + entry_unit + ".wasm";
|
||||
String wasm_path = compiler_unit_wasm_path(context, entry_unit);
|
||||
struct stat wasm_st;
|
||||
f64 phase_started = time_precise();
|
||||
context->stats.wasm_ready_check_count++;
|
||||
@ -133,7 +133,7 @@ static bool wasm_artifact_exists(Request* context, const String& entry_unit)
|
||||
{
|
||||
context->stats.wasm_ready_freshness_us += (u64)((time_precise() - phase_started) * 1000000.0);
|
||||
compiler_prioritize_unit(context, entry_unit);
|
||||
return(compiler_request_can_serve_stale_artifact(context));
|
||||
return(compiler_unit_can_serve_stale_artifact(context, entry_unit));
|
||||
}
|
||||
context->stats.wasm_ready_freshness_us += (u64)((time_precise() - phase_started) * 1000000.0);
|
||||
if(source_missing)
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
// into core.wasm.
|
||||
|
||||
#define __UCE_WASM_CORE__ 1
|
||||
#include "abi.h"
|
||||
#include "../lib/uce_lib.cpp"
|
||||
|
||||
#include "../lib/mysql-connector.h"
|
||||
@ -809,7 +810,7 @@ void uce_free(void* ptr)
|
||||
|
||||
u32 uce_wasm_core_abi_version()
|
||||
{
|
||||
return(7);
|
||||
return(UCE_WASM_CORE_ABI_VERSION);
|
||||
}
|
||||
|
||||
int uce_wasm_core_init()
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// W1 smoke driver for the production UCE core.wasm.
|
||||
|
||||
#include <wasm.h>
|
||||
#include "abi.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
@ -254,7 +255,7 @@ int main(int argc, char** argv)
|
||||
|
||||
CHECK(call_i32(core, "uce_wasm_core_init") == 0, "core init failed");
|
||||
call_i32(core, "uce_wasm_core_reset_request");
|
||||
CHECK(call_i32(core, "uce_wasm_core_abi_version") == 7, "unexpected ABI version");
|
||||
CHECK(call_i32(core, "uce_wasm_core_abi_version") == UCE_WASM_CORE_ABI_VERSION, "unexpected ABI version");
|
||||
|
||||
wasm_memory_t* memory = core.memory();
|
||||
int32_t root = call_i32(core, "uce_dv_root");
|
||||
@ -302,7 +303,7 @@ int main(int argc, char** argv)
|
||||
int32_t output_ptr = call_i32(core, "uce_wasm_output_data");
|
||||
CHECK(read_bytes(memory, output_ptr, output_len) == out, "output plumbing mismatch");
|
||||
|
||||
printf("W1 core.wasm smoke: abi=7 encoded=%d output=%d\n", encoded_len, output_len);
|
||||
printf("W1 core.wasm smoke: abi=%d encoded=%d output=%d\n", UCE_WASM_CORE_ABI_VERSION, encoded_len, output_len);
|
||||
printf("W1 EXIT CRITERION: PASS\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
@ -2615,8 +2615,8 @@ private:
|
||||
{
|
||||
auto artifact_start = std::chrono::steady_clock::now();
|
||||
bool artifact_exists = file_exists_host(worker.unit_wasm_path(resolved));
|
||||
bool can_serve_stale = compiler_request_can_serve_stale_artifact(context);
|
||||
if(can_serve_stale && read_request && !component_source_generation_checked)
|
||||
bool stale_policy = compiler_request_can_serve_stale_artifact(context);
|
||||
if(stale_policy && read_request && !component_source_generation_checked)
|
||||
{
|
||||
component_source_generation = compiler_source_generation(context);
|
||||
component_source_generation_checked = true;
|
||||
@ -2629,7 +2629,7 @@ private:
|
||||
// HTTP reads may serve a complete stale artifact, so bound their graph
|
||||
// stat work; CLI and mutations always check the current graph.
|
||||
bool generation_available = component_source_generation != "";
|
||||
bool check_freshness = !can_serve_stale || !read_request ||
|
||||
bool check_freshness = !stale_policy || !read_request ||
|
||||
cached_freshness == worker.component_freshness.end() ||
|
||||
(generation_available ?
|
||||
cached_freshness->second.source_generation != component_source_generation :
|
||||
@ -2637,13 +2637,14 @@ private:
|
||||
now - cached_freshness->second.checked_at).count() >= 1000);
|
||||
if(check_freshness)
|
||||
{
|
||||
stale = compiler_unit_needs_recompile(context, resolved, 0, can_serve_stale && read_request, true);
|
||||
stale = compiler_unit_needs_recompile(context, resolved, 0, stale_policy && read_request, true);
|
||||
worker.component_freshness[resolved] = { now, stale, component_source_generation };
|
||||
}
|
||||
else
|
||||
stale = cached_freshness->second.stale;
|
||||
}
|
||||
if(stale && can_serve_stale)
|
||||
bool can_serve_stale = stale && compiler_unit_can_serve_stale_artifact(context, resolved);
|
||||
if(stale && stale_policy)
|
||||
{
|
||||
compiler_prioritize_unit(context, resolved);
|
||||
if(!read_request)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user