Build boxes: the slot holder keeps its own line (a keeper, with its self-test in the gate); an explicit --jobs is clamped for bounded classes; one git remote per box; the host-file double suffix
The dashboard lane, 7 October 2026 10:39Z: both slots of build-1 flock-held and EMPTY while two suites ran. Cause: a run from a worktree without last night's append-mode fix opens a busy sibling's slot file with > on every probe. The holder now keeps its own line: a keeper re-writes it within BR_KEEP_S (20 s) whenever the file is empty, until release; remote-run.sh --self-test-keeper (in the gate) truncates a held line and sees it return, and sees nothing written after release; live on build-1 at 11:19Z (the line came back in 25 s). The first version deadlocked the runner's bare wait with the keeper (build-2's first run hung 15 min after its test passed): the keeper stops before the wait. The two running suites' -j 90 came from explicit --jobs 90: a bounded class now clamps it to its cap with a log line (pass --priority gate for the full set). run-from-mac.sh --box N: the host file was suffixed twice (build-server-2-2) and every box's mirror would have shared one remote name; one remote per box (build-N). build-2's first green run: a suite at nice 10 on 32 cores, jobs 32, 986 s cold. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
b94a3d1cbf
commit
97255a4e8f
4 changed files with 44 additions and 10 deletions
|
|
@ -110,6 +110,21 @@ checkout_tree() {
|
||||||
set +e
|
set +e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if [ "${1:-}" = --self-test-keeper ]; then
|
||||||
|
# the keeper restores a truncated holder line within its interval, and stops at release
|
||||||
|
t=$(mktemp -d); trap 'rm -rf "$t"' EXIT
|
||||||
|
BR_PID=$$; BR_LABEL="keeper self-test"; got=0; waited=3; SLOTS_DIR="$t"; BR_KEEP_S=1
|
||||||
|
holder_line() { printf 'pid %s since %sZ waited %s s: %s\n' "$BR_PID" "$(date -u +%H:%M:%S)" "$1" "$BR_LABEL"; }
|
||||||
|
holder_line "$waited" > "$t/build-0"
|
||||||
|
keeper_pid=""; keep_line() { local f="$1" w="$2"; ( while kill -0 "$BR_PID" 2>/dev/null; do [ -s "$f" ] || holder_line "$w" > "$f" 2>/dev/null; sleep "${BR_KEEP_S:-20}"; done ) & keeper_pid=$!; }
|
||||||
|
keep_line "$t/build-0" "$waited"
|
||||||
|
: > "$t/build-0"; sleep 2.5
|
||||||
|
grep -q "^pid $$ since .* waited 3 s: keeper self-test$" "$t/build-0" || { echo "keeper self-test: the truncated holder line was NOT restored"; kill "$keeper_pid" 2>/dev/null; exit 1; }
|
||||||
|
kill "$keeper_pid" 2>/dev/null; wait "$keeper_pid" 2>/dev/null; : > "$t/build-0"; sleep 2.5
|
||||||
|
[ ! -s "$t/build-0" ] || { echo "keeper self-test: the keeper kept writing after release"; exit 1; }
|
||||||
|
echo "keeper self-test: a truncated holder line comes back within the interval; nothing is written after release"; exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "${1:-}" = --self-test ]; then
|
if [ "${1:-}" = --self-test ]; then
|
||||||
t=$(mktemp -d); trap 'rm -rf "$t"' EXIT
|
t=$(mktemp -d); trap 'rm -rf "$t"' EXIT
|
||||||
git init -q --bare -b master "$t/mirror.git"
|
git init -q --bare -b master "$t/mirror.git"
|
||||||
|
|
@ -369,7 +384,17 @@ else
|
||||||
echo "build-remote: holding build-$got on $BR_HOST (waited $waited s; $held of $slots slots held, CARGO_BUILD_JOBS=$BR_JOBS, kind ${BR_KIND:-other}, nice ${BR_NICE:-0}, cores $( [ "${BR_CORES:-0}" = 0 ] && nproc || echo "$BR_CORES"))" >&2
|
echo "build-remote: holding build-$got on $BR_HOST (waited $waited s; $held of $slots slots held, CARGO_BUILD_JOBS=$BR_JOBS, kind ${BR_KIND:-other}, nice ${BR_NICE:-0}, cores $( [ "${BR_CORES:-0}" = 0 ] && nproc || echo "$BR_CORES"))" >&2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
release_slot() { if [ "$got" = measure ]; then : > "$SLOTS_DIR/measure"; else : > "$SLOTS_DIR/build-$got"; fi; }
|
# The holder keeps its own line (the watcher-trust rule, 7 October 2026 10:39Z: two held slots read EMPTY while two suites ran,
|
||||||
|
# because a run from a worktree without last night's append-mode fix still opens a busy sibling's slot file with `>` on every
|
||||||
|
# probe). A keeper re-writes the holder line whenever it finds the file empty, every BR_KEEP_S seconds, until release_slot.
|
||||||
|
keeper_pid=""
|
||||||
|
keep_line() { # <file> <waited>
|
||||||
|
local f="$1" w="$2"
|
||||||
|
( while kill -0 "$BR_PID" 2>/dev/null; do [ -s "$f" ] || holder_line "$w" > "$f" 2>/dev/null; sleep "${BR_KEEP_S:-20}"; done ) &
|
||||||
|
keeper_pid=$!
|
||||||
|
}
|
||||||
|
if [ "$got" = measure ]; then keep_line "$SLOTS_DIR/measure" "$waited"; else keep_line "$SLOTS_DIR/build-$got" "$waited"; fi
|
||||||
|
release_slot() { [ -n "$keeper_pid" ] && { kill "$keeper_pid" 2>/dev/null; wait "$keeper_pid" 2>/dev/null; keeper_pid=""; }; if [ "$got" = measure ]; then : > "$SLOTS_DIR/measure"; else : > "$SLOTS_DIR/build-$got"; fi; }
|
||||||
cd "$BR_DIR" || { BR_CLASS=no-dir jsonlog 2 "$got" "$waited" "" "$(date +%s)" "" "" "" "" "" ""; redlog 2 0 no-dir; release_slot; exit 2; }
|
cd "$BR_DIR" || { BR_CLASS=no-dir jsonlog 2 "$got" "$waited" "" "$(date +%s)" "" "" "" "" "" ""; redlog 2 0 no-dir; release_slot; exit 2; }
|
||||||
|
|
||||||
# PRE-FLIGHT for a cargo command (the instant-death class, 6 October 2026): the manifest parses and every -p package exists,
|
# PRE-FLIGHT for a cargo command (the instant-death class, 6 October 2026): the manifest parses and every -p package exists,
|
||||||
|
|
@ -425,6 +450,9 @@ ncpu=$(nproc); cores_str="0-$((ncpu - 1))"
|
||||||
if [ "${BR_CORES:-0}" -gt 0 ] && [ "${BR_CORES}" -lt "$ncpu" ]; then cores_str="$((ncpu - BR_CORES))-$((ncpu - 1))"; fi
|
if [ "${BR_CORES:-0}" -gt 0 ] && [ "${BR_CORES}" -lt "$ncpu" ]; then cores_str="$((ncpu - BR_CORES))-$((ncpu - 1))"; fi
|
||||||
( [ "${BR_NICE:-0}" -gt 0 ] && renice -n "$BR_NICE" -p $BASHPID >/dev/null 2>&1; [ "$cores_str" != "0-$((ncpu - 1))" ] && taskset -cp "$cores_str" $BASHPID >/dev/null 2>&1; eval "$BR_CMD" ) > >(tee -a "$BR_RUN_LOG") 2> >(tee -a "$BR_RUN_LOG" >&2)
|
( [ "${BR_NICE:-0}" -gt 0 ] && renice -n "$BR_NICE" -p $BASHPID >/dev/null 2>&1; [ "$cores_str" != "0-$((ncpu - 1))" ] && taskset -cp "$cores_str" $BASHPID >/dev/null 2>&1; eval "$BR_CMD" ) > >(tee -a "$BR_RUN_LOG") 2> >(tee -a "$BR_RUN_LOG" >&2)
|
||||||
rc=$?
|
rc=$?
|
||||||
|
# the keeper stops BEFORE the bare `wait` (which flushes the two tees): a bare wait also waits for the keeper, and the keeper waits
|
||||||
|
# for this script, a deadlock that held build-2's first run 15 minutes after its test had passed (7 Oct 2026, 11:04 to 11:19Z)
|
||||||
|
if [ -n "$keeper_pid" ]; then kill "$keeper_pid" 2>/dev/null; wait "$keeper_pid" 2>/dev/null; keeper_pid=""; fi
|
||||||
wait
|
wait
|
||||||
t2=$(date +%s); secs=$(( t2 - t1 ))
|
t2=$(date +%s); secs=$(( t2 - t1 ))
|
||||||
exec_after=$(stat_field "Compile requests executed"); hits_after=$(stat_field "Cache hits "); misses_after=$(stat_field "Cache misses ")
|
exec_after=$(stat_field "Compile requests executed"); hits_after=$(stat_field "Cache hits "); misses_after=$(stat_field "Cache misses ")
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,9 @@ WIRE_ONLY=0; [ "${1:-}" = --wire-only ] && WIRE_ONLY=1
|
||||||
# box N is igneum-build-N with its own dashboard feed name build-N.igneum.network (the dashboard lane's collector reads one server
|
# box N is igneum-build-N with its own dashboard feed name build-N.igneum.network (the dashboard lane's collector reads one server
|
||||||
# section per box; main adds the A record with the IP)
|
# section per box; main adds the A record with the IP)
|
||||||
[ "$BOX" = 1 ] || { BOX_HOSTNAME="${BOX_HOSTNAME:-igneum-build-$BOX}"; WORKERS_HOST="${WORKERS_HOST:-build-$BOX.igneum.network}"; export BOX_HOSTNAME WORKERS_HOST; }
|
[ "$BOX" = 1 ] || { BOX_HOSTNAME="${BOX_HOSTNAME:-igneum-build-$BOX}"; WORKERS_HOST="${WORKERS_HOST:-build-$BOX.igneum.network}"; export BOX_HOSTNAME WORKERS_HOST; }
|
||||||
BS_HOST_FILE=$(bs_box_file "$BOX"); export BS_BOX="$BOX" # lib.sh's bs_host reads BS_BOX
|
export BS_BOX="$BOX" # lib.sh's bs_host derives the host file from BS_BOX (box 1: build-server; box N: build-server-N); never set BS_HOST_FILE here (7 Oct 2026: a second suffix, build-server-2-2)
|
||||||
|
HOST_FILE=$(bs_box_file "$BOX")
|
||||||
|
REMOTE="build"; [ "$BOX" = 1 ] || REMOTE="build-$BOX" # one git remote per box in the two repositories
|
||||||
# the toolchain pin travels from rust-toolchain.toml unless RUST_TOOLCHAIN is set by hand (one file pins every side, 7 Oct 2026)
|
# the toolchain pin travels from rust-toolchain.toml unless RUST_TOOLCHAIN is set by hand (one file pins every side, 7 Oct 2026)
|
||||||
[ -n "${RUST_TOOLCHAIN:-}" ] || RUST_TOOLCHAIN=$(sed -n 's/^channel *= *"\([^"]*\)".*/\1/p' "$REPO/rust-toolchain.toml" 2>/dev/null | head -1); export RUST_TOOLCHAIN
|
[ -n "${RUST_TOOLCHAIN:-}" ] || RUST_TOOLCHAIN=$(sed -n 's/^channel *= *"\([^"]*\)".*/\1/p' "$REPO/rust-toolchain.toml" 2>/dev/null | head -1); export RUST_TOOLCHAIN
|
||||||
PASS="" # a string, not an array: bash 3.2 (the Mac) treats an empty array as unbound under set -u
|
PASS="" # a string, not an array: bash 3.2 (the Mac) treats an empty array as unbound under set -u
|
||||||
|
|
@ -48,19 +50,19 @@ if [ "$WIRE_ONLY" = 0 ]; then
|
||||||
if "${ROOT_SSH[@]}" 'hostname' 2>/dev/null | grep -q '^rescue'; then bs_die "still in the rescue system after provision.sh"; fi
|
if "${ROOT_SSH[@]}" 'hostname' 2>/dev/null | grep -q '^rescue'; then bs_die "still in the rescue system after provision.sh"; fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "$(dirname "$BS_HOST_FILE")"
|
mkdir -p "$(dirname "$HOST_FILE")"
|
||||||
printf 'build@%s\n' "$IP" > "$BS_HOST_FILE"
|
printf 'build@%s\n' "$IP" > "$HOST_FILE"
|
||||||
bs_log "wrote $BS_HOST_FILE"
|
bs_log "wrote $HOST_FILE"
|
||||||
bs_host
|
bs_host
|
||||||
bs_ssh 'hostname; nproc' >/dev/null || bs_die "build@$IP does not answer with $BS_KEY"
|
bs_ssh 'hostname; nproc' >/dev/null || bs_die "build@$IP does not answer with $BS_KEY"
|
||||||
|
|
||||||
wire_remote() { # repo-dir mirror label
|
wire_remote() { # repo-dir mirror label
|
||||||
local dir="$1" mirror="$2" label="$3" url="$BS_HOST:$2" cur
|
local dir="$1" mirror="$2" label="$3" url="$BS_HOST:$2" cur
|
||||||
cur=$(git -C "$dir" remote get-url build 2>/dev/null || true)
|
cur=$(git -C "$dir" remote get-url "$REMOTE" 2>/dev/null || true)
|
||||||
if [ -z "$cur" ]; then git -C "$dir" remote add build "$url"; bs_log "$label: remote build = $url"
|
if [ -z "$cur" ]; then git -C "$dir" remote add "$REMOTE" "$url"; bs_log "$label: remote $REMOTE = $url"
|
||||||
elif [ "$cur" != "$url" ]; then git -C "$dir" remote set-url build "$url"; bs_log "$label: remote build -> $url"
|
elif [ "$cur" != "$url" ]; then git -C "$dir" remote set-url "$REMOTE" "$url"; bs_log "$label: remote $REMOTE -> $url"
|
||||||
else bs_log "$label: remote build ok"; fi
|
else bs_log "$label: remote $REMOTE ok"; fi
|
||||||
GIT_SSH_COMMAND="$BS_SSH_CMD" git -C "$dir" push -q --force build --all && bs_log "$label: every branch pushed to $mirror ($(git -C "$dir" branch --list | wc -l | tr -d ' ') branches)" || bs_die "$label: push to $mirror failed"
|
GIT_SSH_COMMAND="$BS_SSH_CMD" git -C "$dir" push -q --force "$REMOTE" --all && bs_log "$label: every branch pushed to $mirror ($(git -C "$dir" branch --list | wc -l | tr -d ' ') branches)" || bs_die "$label: push to $mirror failed"
|
||||||
}
|
}
|
||||||
wire_remote "$MAIN_REPO" "$BS_MIRROR_REPO" "igneum"
|
wire_remote "$MAIN_REPO" "$BS_MIRROR_REPO" "igneum"
|
||||||
wire_remote "$MAIN_REPO/vendor/igneum-node" "$BS_MIRROR_NODE" "igneum-node (the fork)"
|
wire_remote "$MAIN_REPO/vendor/igneum-node" "$BS_MIRROR_NODE" "igneum-node (the fork)"
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,9 @@ case "${CARGO_ARGS[0]:-build}" in
|
||||||
esac
|
esac
|
||||||
if [ "$PRIORITY" = gate ]; then BR_NICE=0; BR_CORES=0; BR_JOBS_CAP=0
|
if [ "$PRIORITY" = gate ]; then BR_NICE=0; BR_CORES=0; BR_JOBS_CAP=0
|
||||||
elif [ "$SCHED_CLASS" = suite ] || [ "$SCHED_CLASS" = bench ]; then BR_NICE=10; BR_CORES=32; BR_JOBS_CAP=32; fi
|
elif [ "$SCHED_CLASS" = suite ] || [ "$SCHED_CLASS" = bench ]; then BR_NICE=10; BR_CORES=32; BR_JOBS_CAP=32; fi
|
||||||
|
# an explicit --jobs above the bounded class's cap is clamped (main's rule: bounded unless a priority flag; 7 Oct 2026: two suites
|
||||||
|
# ran at -j 90 on a box at load 190 because their callers passed --jobs 90)
|
||||||
|
if [ "$BR_JOBS_CAP" -gt 0 ] && [ -n "$JOBS" ] && [ "$JOBS" -gt "$BR_JOBS_CAP" ]; then bs_log "--jobs $JOBS clamped to $BR_JOBS_CAP for a $SCHED_CLASS (pass --priority gate for the full set)"; JOBS=$BR_JOBS_CAP; fi
|
||||||
export BR_NICE BR_CORES BR_JOBS_CAP BR_PRIORITY
|
export BR_NICE BR_CORES BR_JOBS_CAP BR_PRIORITY
|
||||||
if [ "$PLAN" = 1 ]; then
|
if [ "$PLAN" = 1 ]; then
|
||||||
k="$SCHED_CLASS"; [ "$PRIORITY" = gate ] && k=gate
|
k="$SCHED_CLASS"; [ "$PRIORITY" = gate ] && k=gate
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ tree_checks() {
|
||||||
run "root prover playbooks kill the GPU server and unlink its socket" bash tools/ci/prover-socket-check.sh
|
run "root prover playbooks kill the GPU server and unlink its socket" bash tools/ci/prover-socket-check.sh
|
||||||
run "commit-string gate self-test" bash tools/ci/commit-string-check.sh --self-test
|
run "commit-string gate self-test" bash tools/ci/commit-string-check.sh --self-test
|
||||||
run "build server remote checkout self-test" bash infra/build-server/remote-run.sh --self-test
|
run "build server remote checkout self-test" bash infra/build-server/remote-run.sh --self-test
|
||||||
|
run "a slot holder keeps its own line for the whole run (the watcher-trust rule)" bash infra/build-server/remote-run.sh --self-test-keeper
|
||||||
run "the remote checkout resets the mirror's tree before the branch checkout (the stale-overlay class)" bash -c 'bash tools/ci/mirror-reset-check.sh --self-test && bash tools/ci/mirror-reset-check.sh'
|
run "the remote checkout resets the mirror's tree before the branch checkout (the stale-overlay class)" bash -c 'bash tools/ci/mirror-reset-check.sh --self-test && bash tools/ci/mirror-reset-check.sh'
|
||||||
run "the remote checkout's clean spares a lane's scratch (.igneum-scratch-spare, the fixed prefixes, never -x; the lost-scratch class)" bash -c 'bash tools/ci/scratch-spare-check.sh --self-test && bash tools/ci/scratch-spare-check.sh'
|
run "the remote checkout's clean spares a lane's scratch (.igneum-scratch-spare, the fixed prefixes, never -x; the lost-scratch class)" bash -c 'bash tools/ci/scratch-spare-check.sh --self-test && bash tools/ci/scratch-spare-check.sh'
|
||||||
run "long-running tools keep their body in one parsed block (the edited-while-running class)" bash -c 'bash tools/ci/whole-body-check.sh --self-test && bash tools/ci/whole-body-check.sh'
|
run "long-running tools keep their body in one parsed block (the edited-while-running class)" bash -c 'bash tools/ci/whole-body-check.sh --self-test && bash tools/ci/whole-body-check.sh'
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue