agents: read the memory cap at boot, not from the build
An agent's memory cap was rendered into its flake (`memoryMaxBytes` → `services.hyperhive.agent.claudeMemoryMaxBytes`), so the closure depended on it and a changed cap reached the JSC heap ceiling and the subagent `MemoryHigh=` only after a rebuild. hive-c0re now writes the effective `MemoryMax=` in bytes to the agent's runtime dir (`/run/hive/memory-max-bytes` inside) in `write_dropins`, from the same value the drop-in gets. The file is removed for `infinity` or a RAM percentage, and left alone when the drop-in is kept. Inside the container, `nix/agent-modules/memory-cap.nix` reads it at boot: an environment generator sets `BUN_JSC_forceRAMSize` (75%) for every unit and `machinectl shell`, `/etc/set-environment` exports it in login shells, and a unit generator writes `MemoryHigh=` (2/3) as a drop-in for `hive-subagent-daemon`. The render drops `memoryMaxBytes`, the agent option is removed (both spellings fail eval with a removal message), and `effective_memory_bytes_from` goes with its only caller. Part of #5081 (slice S0-2).
This commit is contained in:
parent
c8df666bf3
commit
0d619e215e
15 changed files with 416 additions and 287 deletions
42
nix/script-tests/agent-memory-cap.nix
Normal file
42
nix/script-tests/agent-memory-cap.nix
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# `checks.script-test-agent-memory-cap` — runs the three readers of
|
||||
# `/run/hive/memory-max-bytes` in ../agent-modules/memory-cap.nix: the
|
||||
# environment generator, the unit generator, and the login-shell snippet
|
||||
# `/etc/set-environment` sources. The cases are in ./agent-memory-cap.sh.
|
||||
#
|
||||
# What runs is the rendered scripts. The one edit is the cap file's path, moved
|
||||
# under the build directory, and the snippet's reference to the environment
|
||||
# generator, pointed at that edited copy.
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
self,
|
||||
nixosSystem,
|
||||
}:
|
||||
let
|
||||
inherit
|
||||
(import ../module-eval/lib.nix {
|
||||
inherit
|
||||
pkgs
|
||||
lib
|
||||
self
|
||||
nixosSystem
|
||||
;
|
||||
})
|
||||
agentWith
|
||||
;
|
||||
|
||||
machine = agentWith { };
|
||||
in
|
||||
pkgs.runCommand "hyperhive-script-test-agent-memory-cap"
|
||||
{
|
||||
ENV_GENERATOR =
|
||||
machine.environment.etc."systemd/system-environment-generators/50-hyperhive-memory-cap".source;
|
||||
UNIT_GENERATOR = machine.systemd.generators.hyperhive-memory-cap;
|
||||
PROFILE = machine.environment.etc."hyperhive/memory-cap.sh".source;
|
||||
EXTRA_INIT = machine.environment.extraInit;
|
||||
SHELLS = "${pkgs.dash}/bin/dash ${pkgs.bash}/bin/sh";
|
||||
}
|
||||
''
|
||||
${pkgs.bash}/bin/bash ${./agent-memory-cap.sh}
|
||||
touch "$out"
|
||||
''
|
||||
94
nix/script-tests/agent-memory-cap.sh
Normal file
94
nix/script-tests/agent-memory-cap.sh
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# Cases for ./agent-memory-cap.nix. Each case writes a cap file (or none), runs
|
||||
# both generators with a clean environment and compares what they produced,
|
||||
# then sources the login-shell snippet under each POSIX shell and compares what
|
||||
# a child process sees.
|
||||
set -uo pipefail
|
||||
|
||||
failures=0
|
||||
count=0
|
||||
|
||||
fail() {
|
||||
echo "FAILED: $case: $*" >&2
|
||||
failures=$((failures + 1))
|
||||
}
|
||||
|
||||
dir=$(mktemp -d)
|
||||
cap=$dir/memory-max-bytes
|
||||
sed "s|/run/hive/memory-max-bytes|$cap|g" "$ENV_GENERATOR" >"$dir/env-generator"
|
||||
sed "s|/run/hive/memory-max-bytes|$cap|g" "$UNIT_GENERATOR" >"$dir/unit-generator"
|
||||
chmod +x "$dir/env-generator" "$dir/unit-generator"
|
||||
sed "s|$ENV_GENERATOR|$dir/env-generator|g" "$PROFILE" >"$dir/profile"
|
||||
envbin=$(command -v env)
|
||||
|
||||
case="setup"
|
||||
for g in env-generator unit-generator; do
|
||||
if grep -q '/run/hive/memory-max-bytes' "$dir/$g"; then fail "$g still names /run/hive after the rewrite"; fi
|
||||
if ! grep -qF "$cap" "$dir/$g"; then fail "the rewrite did not reach $g's read"; fi
|
||||
done
|
||||
if ! grep -qF "$dir/env-generator" "$dir/profile"; then fail "the snippet does not call the rewritten generator"; fi
|
||||
if ! printf '%s\n' "$EXTRA_INIT" | grep -qxF '. /etc/hyperhive/memory-cap.sh'; then
|
||||
fail "/etc/set-environment does not source the snippet"
|
||||
fi
|
||||
|
||||
# check <description> <expected BUN_JSC_forceRAMSize or ""> <expected MemoryHigh or "">
|
||||
check() {
|
||||
case=$1
|
||||
count=$((count + 1))
|
||||
local want_env=${2:+BUN_JSC_forceRAMSize=$2} want_high=$3 got
|
||||
got=$(env -i "$dir/env-generator")
|
||||
if [ "$got" != "$want_env" ]; then
|
||||
fail "environment generator printed:"$'\n'"$got"$'\n'"wanted:"$'\n'"$want_env"
|
||||
fi
|
||||
|
||||
local out=$dir/generator-out
|
||||
rm -rf "$out"
|
||||
mkdir -p "$out/normal" "$out/early" "$out/late"
|
||||
env -i "$dir/unit-generator" "$out/normal" "$out/early" "$out/late"
|
||||
local dropin=$out/normal/hive-subagent-daemon.service.d/50-memory-high.conf
|
||||
if [ -z "$want_high" ]; then
|
||||
got=$(find "$out" -type f)
|
||||
if [ -n "$got" ]; then fail "unit generator wrote:"$'\n'"$got"; fi
|
||||
else
|
||||
got=$(cat "$dropin" 2>&1)
|
||||
if [ "$got" != "[Service]"$'\n'"MemoryHigh=$want_high" ]; then
|
||||
fail "unit generator's drop-in:"$'\n'"$got"
|
||||
fi
|
||||
fi
|
||||
|
||||
local sh
|
||||
for sh in $SHELLS; do
|
||||
# Only what the snippet exported reaches the child's environment. No PATH:
|
||||
# the snippet must not need one.
|
||||
got=$(env -i "$sh" -c ". '$dir/profile'; exec '$envbin'" | grep -E '^(BUN_|LD_PRELOAD=|__hyperhive)')
|
||||
if [ "$got" != "$want_env" ]; then
|
||||
fail "$sh: a child of the sourcing shell saw:"$'\n'"$got"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
printf '8589934592\n' >"$cap"
|
||||
check "an 8G cap" 6442450944 5726623061
|
||||
|
||||
printf '4294967296' >"$cap"
|
||||
check "a line without a newline is kept" 3221225472 2863311530
|
||||
|
||||
printf '4294967296\nLD_PRELOAD=/x\n' >"$cap"
|
||||
check "only the first line is read" 3221225472 2863311530
|
||||
|
||||
printf '8G\n' >"$cap"
|
||||
check "a size with a suffix is ignored" "" ""
|
||||
|
||||
printf '0123\n' >"$cap"
|
||||
check "a leading zero is ignored" "" ""
|
||||
|
||||
printf '1234567890123456\n' >"$cap"
|
||||
check "sixteen digits are ignored" "" ""
|
||||
|
||||
: >"$cap"
|
||||
check "an empty file" "" ""
|
||||
|
||||
rm -f "$cap"
|
||||
check "no file" "" ""
|
||||
|
||||
echo "$count cases, $failures failed"
|
||||
[ "$failures" -eq 0 ]
|
||||
Loading…
Reference in a new issue