diff --git a/check b/check index 6b685e7..619dbdd 100755 --- a/check +++ b/check @@ -5,6 +5,8 @@ # ./check quick tests only # ./check mount full gate + live FUSE mount end-to-end (needs root) # ./check bench full gate + cube-bench correctness-gated microbenchmarks +# ./check stress full gate + ~150s sustained stress against a real daemon +# (override duration with STRESS_SECONDS=NN) # # Exit 0 means the tree on disk is green. This is the single source of truth; # do not claim verification from an ad-hoc run. @@ -16,6 +18,9 @@ # Why `bench` is opt-in: it is a release build + timed run of cube-bench, which # is slow and machine-noise-sensitive; it asserts correctness on every path but # the numbers are informational, not a pass/fail gate. +# Why `stress` is opt-in: it spins a fresh daemon built from this tree and drives +# it hard for ~150s to exercise the per-command latency + per-C telemetry under +# load; the numbers are informational, and it obviously takes ~150s to run. set -euo pipefail cd "$(dirname "$0")" @@ -41,16 +46,30 @@ cargo test --workspace $FEAT --jobs "$JOBS" step "3/3 clippy" cargo clippy --workspace --all-targets $FEAT --jobs "$JOBS" -- -D warnings -[ "${1:-}" = mount ] || { printf '\n\033[32mALL CHECKS PASSED\033[0m (run ./check mount for live FUSE e2e, ./check bench for metrics)\n'; exit 0; } +[ "${1:-}" = mount ] && { run_mount=1; } +[ "${1:-}" = bench ] && { run_bench=1; } +[ "${1:-}" = stress ] && { run_stress=1; } -[ "${1:-}" = bench ] || { printf '\n\033[32mALL CHECKS PASSED\033[0m (run ./check mount for live FUSE e2e, ./check bench for metrics)\n'; exit 0; } +[ -z "${run_mount:-}${run_bench:-}${run_stress:-}" ] && { + printf '\n\033[32mALL CHECKS PASSED\033[0m (run: ./check mount | bench | stress for those stages)\n' + exit 0 +} -step "4/4 cube-bench" -# Release build + timed run. Asserts correctness on every path; numbers are -# informational. JOBS kept modest so we don't saturate the box. -cargo run -p cube-bench --release --jobs "$JOBS" -printf '\n\033[32mALL CHECKS PASSED\033[0m (incl. cube-bench)\n' -exit 0 +if [ "${run_bench:-}" ]; then + step "4/4 cube-bench" + # Release build + timed run. Asserts correctness on every path; numbers are + # informational. JOBS kept modest so we don't saturate the box. + cargo run -p cube-bench --release --jobs "$JOBS" + printf '\n\033[32mALL CHECKS PASSED\033[0m (incl. cube-bench)\n' + exit 0 +fi + +if [ "${run_stress:-}" ]; then + step "4/4 sustained stress ($((${STRESS_SECONDS:-150}))s) against a fresh daemon" + STRESS_SECONDS="${STRESS_SECONDS:-150}" ./tools/stress.sh + printf '\n\033[32mALL CHECKS PASSED\033[0m (incl. sustained stress)\n' + exit 0 +fi step "4/4 live FUSE mount" [ "$(id -u)" -eq 0 ] || { echo "SKIP: live mount needs root" >&2; exit 1; } diff --git a/tools/stress.sh b/tools/stress.sh new file mode 100755 index 0000000..2789bd0 --- /dev/null +++ b/tools/stress.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# Sustained stress stage for CUBELinux-2 (invoked by `./check stress`). +# +# Builds cube-server + cubec from THIS workspace tree, spins a fresh daemon on a +# throwaway socket + store, then drives it hard for STRESS_SECONDS (default 150) +# with real prog/run traffic while sampling the per-command latency + per-C +# namespace telemetry. This exercises the new cubesys `stats` path under load. +# +# It deliberately does NOT touch the production daemon (real socket/store in +# /home/luulu/.cubelinux) so it is safe to run any time. Numbers are +# informational; the stage passes iff the gate passed and the daemon survives. +set -uo pipefail +REPO="$(cd "$(dirname "$0")/.." && pwd)" +cd "$REPO" +export PATH="$HOME/.cargo/bin:/usr/local/bin:/usr/bin:/bin:$PATH" +command -v cargo >/dev/null || { echo "cargo not found" >&2; exit 127; } + +JOBS="${JOBS:-4}" +SECS="${STRESS_SECONDS:-150}" + +echo "building cube-server + cubec from tree ($REPO) ..." +cargo build -p cubesys --release --bins --jobs "$JOBS" >/dev/null 2>&1 || { + echo "BUILD FAILED (cubesys bins)"; exit 1 +} +SRV="$REPO/target/release/cube-server" +CLI="$REPO/target/release/cubec" +[ -x "$SRV" ] && [ -x "$CLI" ] || { echo "binaries missing after build"; exit 1; } + +TMPD="$(mktemp -d /tmp/cube-stress.XXXXXX)" +SOCK="$TMPD/cube.sock" +STORE="$TMPD/store.json" +trap 'kill $DPID 2>/dev/null; wait $DPID 2>/dev/null; rm -rf "$TMPD"' EXIT + +echo "starting stress daemon on $SOCK ..." +"$SRV" --socket "$SOCK" --store "$STORE" >"$TMPD/daemon.log" 2>&1 & +DPID=$! +# wait for socket (max ~15s) +for i in $(seq 1 15); do [ -S "$SOCK" ] && break; sleep 1; done +[ -S "$SOCK" ] || { echo "daemon failed to start (see $TMPD/daemon.log)"; kill $DPID 2>/dev/null; exit 1; } + +START=$(date +%s) +END=$((START + SECS)) +PAIRS=0 +SAMPLE_EVERY=25 # sample stats every 25 pairs (~ once a second at load) +while [ "$(date +%s)" -lt "$END" ]; do + X=$((PAIRS % 256)) + "$CLI" --socket "$SOCK" "prog /c077/z001/y001/x$X const $((PAIRS % 97)) halt" >/dev/null 2>&1 + "$CLI" --socket "$SOCK" "run /c077/z001/y001/x$X" >/dev/null 2>&1 + PAIRS=$((PAIRS + 1)) + if [ "$((PAIRS % SAMPLE_EVERY))" -eq 0 ]; then + echo "--- [$(date -u +%H:%M:%S)] stress sample $((PAIRS / SAMPLE_EVERY)) (pairs=$PAIRS) ---" + "$CLI" --socket "$SOCK" "stats" || true + fi +done + +# final health snapshot +echo "--- final stats (pairs driven: $PAIRS over ~${SECS}s) ---" +"$CLI" --socket "$SOCK" "stats" || true + +ELAPSED=$(($(date +%s) - START)) +if [ "$ELAPSED" -lt 1 ]; then ELAPSED=1; fi +echo "stress throughput: ~$((PAIRS / ELAPSED)) prog+run pairs/s (informational)" +echo "STRESS STAGE OK (daemon alive, $PAIRS pairs driven)" +exit 0