a73x

e4e6dbee

test: soak persistence phase — one daemon, N client lifecycles, RSS/fd bounds

a73x   2026-08-14 09:09

Commit message
test: soak persistence phase — one daemon, N client lifecycles, RSS/fd bounds

fds must return to baseline exactly; RSS gets a 4MB growth bound past a
3-cycle warmup. Catches the classes the Zig-side marker cannot: C-side
growth, fd leaks, unbounded accumulation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

test/soak.sh
Old New
@@ -56,6 +56,67 @@ while [ "$i" -le "$N" ]; do
56 fi 56 fi
57 i=$((i + 1)) 57 i=$((i + 1))
58 done 58 done
59 # ---- persistence phase (hygiene kit, 6b) ----
60 # One daemon, SOAK_CYCLES client lifecycles. Catches what the run loop
61 # structurally cannot: C-side growth, fd leaks, unbounded accumulation —
62 # the classes the Zig-side LEAK marker (6a) never sees. Baseline is taken
63 # AFTER a warmup: the first attaches pay one-time allocations (grid,
64 # history) that are capacity, not leakage.
65 PSOCK="$TMP/mux-soak-persist-$$.sock"
66 PLOG="$TMP/mux-soak-persist-$$.log"
67 CYCLES="${SOAK_CYCLES:-20}"
68 WARMUP=3
69 RSS_BOUND_KB=4096
70 "$MUXD" run --sock "$PSOCK" --shell /bin/sh > "$PLOG" 2>&1 &
71 PDPID=$!
72 _i=0
73 while [ ! -S "$PSOCK" ] && [ "$_i" -lt 100 ]; do sleep 0.05; _i=$((_i + 1)); done
74 if [ ! -S "$PSOCK" ]; then
75 echo "soak FAIL: persistence daemon never bound $PSOCK"
76 FAILED=$((FAILED + 1))
77 printf 'persistence: daemon never bound\n' >> "$SUMMARY"
78 else
79 BASE_RSS=0; BASE_FD=0; RSS=0; FD=0
80 c=1
81 while [ "$c" -le "$CYCLES" ]; do
82 { printf 'echo cycle-%s\n' "$c"; sleep 1; printf '\034'; } | \
83 timeout 30 "$MUX" --sock "$PSOCK" > /dev/null 2>&1
84 RSS=$(awk '/VmRSS/{print $2}' "/proc/$PDPID/status" 2>/dev/null || echo 0)
85 FD=$(ls "/proc/$PDPID/fd" 2>/dev/null | wc -l)
86 [ "$c" -eq "$WARMUP" ] && { BASE_RSS=$RSS; BASE_FD=$FD; }
87 c=$((c + 1))
88 done
89 echo "soak persistence: $CYCLES cycles, RSS ${BASE_RSS}->${RSS} kB, fds ${BASE_FD}->${FD}"
90 if [ "$RSS" -eq 0 ] || [ "$BASE_RSS" -eq 0 ]; then
91 echo "soak FAIL: persistence daemon died mid-phase"
92 FAILED=$((FAILED + 1))
93 printf 'persistence: daemon died mid-phase\n' >> "$SUMMARY"
94 else
95 # fds must RETURN to baseline exactly: every attach opens, every
96 # detach must close. RSS gets a bound, not equality — allocators
97 # retain pages — but growth past it over this few cycles is a leak.
98 if [ "$FD" -ne "$BASE_FD" ]; then
99 echo "soak FAIL: persistence fd count $BASE_FD -> $FD across detached cycles"
100 FAILED=$((FAILED + 1))
101 printf 'persistence: fd leak %s->%s\n' "$BASE_FD" "$FD" >> "$SUMMARY"
102 fi
103 if [ $((RSS - BASE_RSS)) -gt "$RSS_BOUND_KB" ]; then
104 echo "soak FAIL: persistence RSS grew $((RSS - BASE_RSS)) kB (bound $RSS_BOUND_KB)"
105 FAILED=$((FAILED + 1))
106 printf 'persistence: RSS grew %s kB\n' "$((RSS - BASE_RSS))" >> "$SUMMARY"
107 fi
108 fi
109 kill "$PDPID" 2>/dev/null
110 wait "$PDPID" 2>/dev/null
111 # The daemon's own Zig-side verdict rides along for free (6a).
112 if grep -q "LEAK:" "$PLOG"; then
113 echo "soak FAIL: persistence daemon reported leaked allocations:"
114 grep "LEAK:" "$PLOG"
115 FAILED=$((FAILED + 1))
116 printf 'persistence: LEAK marker\n' >> "$SUMMARY"
117 fi
118 fi
119 rm -f "$PLOG" "$PSOCK"
59 rm -f "$LOG" 120 rm -f "$LOG"
60 echo "---" 121 echo "---"
61 if [ "$FAILED" -eq 0 ]; then 122 if [ "$FAILED" -eq 0 ]; then