]> mop.ddnsfree.com - git repositories - brisk.git/commitdiff
run the bench from the repository instead of a copy
authorMatteo Nastasi <nastasi@alternativeoutput.it>
Sun, 13 Sep 2026 15:08:54 +0000 (15:08 +0000)
committerMatteo Nastasi <nastasi@alternativeoutput.it>
Sun, 13 Sep 2026 15:08:54 +0000 (15:08 +0000)
The bench used to be a copy kept in /root/load, run as root, driven from
outside the container. The paths were written into the scripts and the
copy went stale more than once. They now run straight out of the working
tree, unprivileged, and read common.sh for the three settings they share.

BRISK_WORK holds the working files, /var/tmp/brisk-load by default. Not
/tmp, which is a tmpfs here and gets wiped by a restart of the container
in the middle of a session - that is why the scripts had been moved to
/root/load to begin with - and not the script directory either, or
auth.txt and the captured streams would be left lying in the git tree.
BRISK_LEGAL is where the daemon keeps its log and its pid file, and SUDO
covers the few things that still need root: restarting the service, and
reading the open descriptors of a daemon that runs as another user.

brisk_step.sh no longer starts the daemon by hand. It went through a raw
screen invocation of its own, which is precisely how orphaned instances
used to pile up: the newcomer unlinks the socket files and binds its own
while the first one stays alive on its own shared memory. It restarts the
service now. Its two log counts also become deltas: both logs are
cumulative, so the totals said nothing about the step that just ran.

The trap that reaps the background loops changes shape. It was
"trap 'kill 0'", which signals the whole process group - almost always
wider than the script. It killed the script itself on the way out, so the
exit status was always a death by signal, and it reached whatever else
shared the group: the caller, or the other half of a pipeline, so that
"brisk_step.sh | tail" printed nothing at all. Running everything through
"incus exec" gave each script a session of its own and hid all of it.
bench_reap walks our own descendants instead, which reaches the loops
inside "( ... ) &" and the curls under them and nothing else.

Checked from the working tree: brisk_step.sh completes through a pipe
with its output intact and exit status 0, the sampler reads the daemon
descriptors through sudo, game.sh forms the table, stop.sh reports a
clean machine, and INSTALL.sh installs and restarts from here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fseposbqj9mBbQXoT8KnBj

test/load/README
test/load/brisk_sample.sh
test/load/brisk_step.sh
test/load/common.sh [new file with mode: 0644]
test/load/game.sh
test/load/hand.sh
test/load/partial.py [changed mode: 0644->0755]
test/load/play_human.sh
test/load/ramp_probe.sh
test/load/sit4.sh
test/load/stop.sh

index 9b282a72dfb240af5efa8231d6f389dec4538125..0ec0eb25893dec9703f99a5a3a1904ff21c30dfd 100644 (file)
@@ -30,7 +30,34 @@ game.sh and hand.sh instead use the five users uno/due/tre/qua/cin with the
 passwords one/two/thr/for/fiv.
 
 The paths and ports are those of the test container (/home/brisk, 8444 for
-https): they have to be adapted elsewhere.
+https, 8082 for plain http): they have to be adapted elsewhere.
+
+
+How to run them
+---------------
+
+From this directory, straight out of the repository:
+
+  ./brisk_step.sh 150 90 3 --tls
+  ./stop.sh
+
+They used to be a copy kept in /root/load, run as root, with those paths
+written into them; the copy went stale more than once. Now every script finds
+its own directory and reads common.sh, which holds the three settings they all
+share:
+
+  BRISK_WORK    where the working files go.  Default /var/tmp/brisk-load.
+                NOT /tmp: that is a tmpfs here, and a restart of the container
+                wipes it in the middle of a session. Not this directory either,
+                or auth.txt and the captured streams would be left in the git
+                tree.
+  BRISK_LEGAL   where the daemon keeps its log and its pid file: the path
+                given to INSTALL.sh with -l.  Default /home/brisk/legal.
+  SUDO          the daemon runs as another user, so restarting the service and
+                reading its open descriptors need root while the bench itself
+                does not. Set SUDO= to empty when already running as root.
+
+Nothing here needs to run as root.
 
 
 The load
@@ -112,14 +139,27 @@ At the end of a session
 It shuts everything down and prints the state: running scripts, running curls,
 descriptors of the daemon, disk space, load. To be run ALWAYS.
 
-The scripts that start background loops carry a trap that kills their own
-process group. Without it the loops outlive their parent and are left spinning
-for nothing: that happened for two days, with eighty processes alive and the
-machine load at 19.
-
-stop.sh does not use "pkill -f": the pattern would end up in the command line
-of the script itself, which would kill itself and leave the targets alive.
-
-The working files (auth.txt, tok.txt, mani.txt, *.stream) are created in the
-working directory of the scripts and can grow large: the read streams grow all
-the time. stop.sh deletes them.
+The scripts that start background loops carry a trap, bench_reap, that kills
+their own descendants on the way out. Without it the loops outlive their
+parent and are left spinning for nothing: that happened for two days, with
+eighty processes alive and the machine load at 19.
+
+It used to be "trap 'kill 0'", which signals the whole process group. That is
+almost always wider than the script: it killed the script itself, so the exit
+status was always a death by signal, and it reached whatever else shared the
+group - whoever launched it, or the other half of a pipeline, so that
+"brisk_step.sh | tail" printed nothing at all. Running everything through
+"incus exec" gave each script a session of its own and hid all of it.
+
+Two traps of the same family, both of which have already cost time here:
+
+  - stop.sh does not use "pkill -f": the pattern would end up in the command
+    line of the script itself, which would kill itself and leave the targets
+    alive.
+  - when counting leftovers by hand, the grep matches its own command line.
+    Split the pattern ("ga""me.sh") or use "[g]ame.sh", otherwise a clean
+    machine looks busy and a busy one looks clean.
+
+The working files (auth.txt, tok.txt, mani.txt, *.stream) are created in
+BRISK_WORK and can grow large: the read streams grow all the time. stop.sh
+deletes them.
index 79ce2a9979ed1bcc4a3bdea5cb61108654e52964..42840ffa9fb8546aa05c9e064b6da69def18747f 100755 (executable)
@@ -2,6 +2,7 @@
 # Samples the resources of daemon and frontend during a load test.
 # usage: brisk_sample.sh <seconds> <interval> <label>
 DUR="${1:-60}"; INT="${2:-5}"; TAG="${3:-x}"
+. "$(dirname "$0")/common.sh"
 
 dpid="$(pgrep -f 'php \./brisk-spush\.php' | head -1)"
 if [ -z "$dpid" ]; then echo "daemon not found"; exit 1; fi
@@ -20,7 +21,7 @@ end=$(( $(date +%s) + DUR ))
 while [ $(date +%s) -lt $end ]; do
     sleep "$INT"
     rss=$(awk '/VmRSS/{print $2}' /proc/$dpid/status 2>/dev/null || echo 0)
-    fd=$(ls /proc/$dpid/fd 2>/dev/null | wc -l)
+    fd=$($SUDO ls /proc/$dpid/fd 2>/dev/null | wc -l)
     conn=$(ss -x 2>/dev/null | grep -c 'brisk[0-9]*\.sock')
     [ "$rss" -gt "$maxrss" ] && maxrss=$rss
     [ "$fd" -gt "$maxfd" ] && maxfd=$fd
index 5463a521cd1f1b985e35d3a43388ef81da7571d7..e5357b48ab401e9563dfabbe8ed03bb5b2fb043a 100755 (executable)
@@ -4,9 +4,13 @@
 #
 # usage: brisk_step.sh <clients> <duration> <write_period> [--silent] [--tls]
 # Subprocess loops outlive their parent: without this they are left orphaned,
-# spinning for nothing (it happened: eighty loops alive for two days). kill 0
-# kills the process group, which setsid makes exclusive to this script.
-trap "kill 0" EXIT INT TERM
+# spinning for nothing (it happened: eighty loops alive for two days).
+#
+# bench_reap walks our descendants, so it reaches them without touching the
+# caller or the other half of a pipeline. See common.sh.
+. "$(dirname "$0")/common.sh"
+trap bench_reap EXIT INT TERM
+
 
 N="${1:-50}"; DUR="${2:-120}"; PER="${3:-10}"; shift 3
 EXTRA="$*"
@@ -14,20 +18,29 @@ WARM=30
 PORT="${PORT:-8082}"
 echo "$EXTRA" | grep -q -- "--tls" && PORT=8444
 
-# empty room: the users of the previous step would stay connected
-p=$(pgrep -f 'php \./brisk-spush\.php'); [ -n "$p" ] && kill $p; sleep 2
-s=$(pgrep -u www-data -x screen); [ -n "$s" ] && kill $s 2>/dev/null; sleep 1
-rm -f /tmp/brisk.log
-su -s /bin/bash www-data -c "cd /home/brisk/web/brisk/spush && screen -d -m -S brisk -L -Logfile /tmp/brisk.log ./brisk-spush.php"
+# Empty room: the users of the previous step would stay connected.
+#
+# Through the init script, never by hand. Starting the daemon beside one that
+# is already running is what used to leave orphaned instances behind: the
+# newcomer unlinks the socket files and binds its own, taking every new
+# connection, while the first one stays alive on its own shared memory and
+# nothing says so. "stop" also closes the screen sessions, which carry the
+# loop that would otherwise respawn what it just killed.
+LOG0=$(wc -l < "$BRISK_LEGAL/brisk.log" 2>/dev/null || echo 0)
+NGX0=$($SUDO grep -cE ' \[(error|crit|alert)\] ' /var/log/nginx/error.log 2>/dev/null || echo 0)
+$SUDO systemctl restart brisk
 sleep 4
 
 echo "=== $N clients, $DUR s, one write every $PER s $EXTRA ==="
-( sleep $((WARM + 3)); /root/load/brisk_sample.sh "$DUR" 5 "$N" ) > /tmp/sample.out 2>&1 &
+( sleep $((WARM + 3)); "$BENCH/brisk_sample.sh" "$DUR" 5 "$N" ) > "$BRISK_WORK/sample.out" 2>&1 &
 SAMP=$!
-cd /root/load
-timeout $((WARM + DUR + 120)) python3 brisk_load.py --auth --clients "$N" \
+timeout $((WARM + DUR + 120)) python3 "$BENCH/brisk_load.py" --auth --clients "$N" \
         --warmup "$WARM" --duration "$DUR" --period "$PER" --port "$PORT" $EXTRA
 wait $SAMP
-cat /tmp/sample.out
-echo "  daemon log: $(wc -l < /tmp/brisk.log) lines, of which unexpected: $(tr -d '#\r' < /tmp/brisk.log | grep -icE 'warning|notice|error|fatal')"
-echo "  nginx errors: $(grep -cE ' \[(error|crit|alert)\] ' /var/log/nginx/error.log 2>/dev/null || echo 0)"
+cat "$BRISK_WORK/sample.out"
+# The log is cumulative, so what counts is what this step added to it
+LOG1=$(wc -l < "$BRISK_LEGAL/brisk.log" 2>/dev/null || echo 0)
+echo "  daemon log: $((LOG1 - LOG0)) new lines, of which unexpected: $(tail -n +$((LOG0 + 1)) "$BRISK_LEGAL/brisk.log" 2>/dev/null | grep -icE 'warning|notice|error|fatal|crit')"
+# the nginx log is 640 www-data:adm, unreadable to the unprivileged bench
+NGX1=$($SUDO grep -cE ' \[(error|crit|alert)\] ' /var/log/nginx/error.log 2>/dev/null || echo 0)
+echo "  nginx errors: $((NGX1 - NGX0)) new"
diff --git a/test/load/common.sh b/test/load/common.sh
new file mode 100644 (file)
index 0000000..41c3522
--- /dev/null
@@ -0,0 +1,63 @@
+# Shared settings of the load bench. Sourced by the scripts here, never run.
+#
+# The bench used to live in /root/load, with those paths written into it, and
+# to run as root. It runs from the repository now, as an unprivileged user, so
+# every script finds its own directory instead and keeps its working files out
+# of it: auth.txt, the captured streams and the logs would otherwise be left
+# lying in the git tree.
+
+BENCH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+# Working files go here, and not in /tmp: that is a tmpfs, and a restart of the
+# container wipes it in the middle of a session, scripts included - which is
+# why they had been moved to /root/load in the first place. /var/tmp is on the
+# root filesystem.
+BRISK_WORK="${BRISK_WORK:-/var/tmp/brisk-load}"
+mkdir -p "$BRISK_WORK"
+
+# Where the daemon keeps its log and its pid file: the path handed to
+# INSTALL.sh with -l. Getting it wrong is not harmless - the init script looks
+# for brisk.pid in there, and while it cannot find it "stop" does nothing and
+# every restart leaves an orphaned daemon behind instead of replacing it.
+BRISK_LEGAL="${BRISK_LEGAL:-/home/brisk/legal}"
+
+# The daemon runs as another user: restarting the service and reading its open
+# descriptors need root, and the bench is unprivileged now.
+SUDO="${SUDO:-sudo}"
+
+# Kills the background loops this script started, and nothing else.
+#
+# The bench used to do: trap "kill 0" EXIT INT TERM. kill 0 signals the whole
+# process group, which is nearly always wider than this script. It killed the
+# script itself on the way out, so the exit status was always a death by
+# signal and buffered output could be lost; and it reached whatever else
+# shared the group - whoever launched us, or the other half of a pipeline,
+# so that "brisk_step.sh | tail" printed nothing at all. Running under
+# "incus exec" gave every script a session of its own and hid all of it.
+#
+# Walking our own descendants reaches exactly what kill 0 was there for - the
+# loops inside "( ... ) &" and the curls underneath them, which inherit us as
+# their parent - and nothing that we did not start ourselves.
+bench_descendants() {
+    local kid
+    for kid in $(pgrep -P "$1" 2>/dev/null); do
+        bench_descendants "$kid"
+        echo "$kid"
+    done
+}
+
+bench_reap() {
+    trap - EXIT INT TERM
+
+    local kids
+    kids=$(bench_descendants $$)
+    # drop them from the job table first, or the shell announces every kill
+    disown -a 2>/dev/null || true
+    if [ -n "$kids" ]; then
+        kill $kids 2>/dev/null
+        sleep 0.3
+        kids=$(bench_descendants $$)
+        [ -n "$kids" ] && kill -9 $kids 2>/dev/null
+    fi
+    return 0
+}
index 5368d20b0d67caa8289260648bb6ac89e228ce04..6a4a54580f24e347fa15d0cb985208690a94f7bb 100755 (executable)
@@ -4,17 +4,19 @@
 #
 # usage: game.sh [table] [port]
 #
-# It lives in /root/load and not in /tmp, which is tmpfs: a restart of the
-# container used to wipe the scripts.
 
 # Subprocess loops outlive their parent: without this they are left orphaned,
-# spinning for nothing. kill 0 kills the group, which setsid makes exclusive.
-trap "kill 0" EXIT INT TERM
+# spinning for nothing (it happened: eighty loops alive for two days).
+#
+# bench_reap walks our descendants, so it reaches them without touching the
+# caller or the other half of a pipeline. See common.sh.
+. "$(dirname "$0")/common.sh"
+trap bench_reap EXIT INT TERM
 
 TAB="${1:-4}"; PORTA="${2:-8444}"
 B="https://127.0.0.1:${PORTA}/brisk"
 CURL="curl -sSk"
-cd /root/load
+cd "$BRISK_WORK"
 rm -f auth.txt r?.stream k?.stream mani.txt tok.txt
 
 echo "== 1. login of five authenticated users =="
index 6d4d6e25aef912de0fa55ca48d33fd5b603d9251..fee87b0d97657359d0510bf15ccc51fce3079a0e 100755 (executable)
@@ -7,7 +7,8 @@
 TAB="${1:-4}"; PORTA="${2:-8444}"
 B="https://127.0.0.1:${PORTA}/brisk"
 CURL="curl -sSk"
-cd /root/load
+. "$(dirname "$0")/common.sh"
+cd "$BRISK_WORK"
 TK=$(cat tok.txt)
 
 send() {
old mode 100644 (file)
new mode 100755 (executable)
index 302330ee7b55e6f2fee0e9ba738bf2d9c680bbb0..02b09309f2d0958179648fe1bffe1e6999ca1346 100755 (executable)
@@ -5,8 +5,10 @@
 # When it is their turn they first try to pass; if after a second the turn is
 # still theirs it means the auction is over, and then they play the first card
 # the server accepts. That way there is no need to know which phase we are in.
-B="https://127.0.0.1:8444/brisk"; TAB="${1:-4}"; cd /tmp
+B="https://127.0.0.1:8444/brisk"; TAB="${1:-4}"
+. "$(dirname "$0")/common.sh"
 CURL="curl -sSk"
+cd "$BRISK_WORK"
 MAXWAIT="${2:-900}"
 
 echo "waiting for the table to form..."
index 7c8ae667431dd6061a07c2eed0daf32f0b74225f..668a860a8c41a393b016fde37474ceac14741eaa 100755 (executable)
@@ -1,17 +1,20 @@
 #!/bin/bash
 # Measures what a client receives while 150 users come into the room.
 # Subprocess loops outlive their parent: without this they are left orphaned,
-# spinning for nothing (it happened: eighty loops alive for two days). kill 0
-# kills the process group, which setsid makes exclusive to this script.
-trap "kill 0" EXIT INT TERM
+# spinning for nothing (it happened: eighty loops alive for two days).
+#
+# bench_reap walks our descendants, so it reaches them without touching the
+# caller or the other half of a pipeline. See common.sh.
+. "$(dirname "$0")/common.sh"
+trap bench_reap EXIT INT TERM
 
 B="http://127.0.0.1:8082/brisk"
 TOK=$(curl -sS -m 10 "$B/index_wr.php?mesg=getchallenge&cli_name=load303" | cut -d"|" -f2)
 MP=$(printf "%s" "load303" | md5sum | cut -d" " -f1)
 PRIV=$(printf "%s%s" "$TOK" "$MP" | md5sum | cut -d" " -f1)
 S=$(curl -sS -m 20 "$B/index.php?name=load303&pass_private=$PRIV" | grep -oE "sess = \"[0-9a-f]+\"" | head -1 | sed "s/.*\"\(.*\)\"/\1/")
-( curl -sS -N -m 60 -b "sess=$S" "$B/index_rd.php?stat=&subst=&step=-1&from=index_php&transp=xhr" > /tmp/ramp.stream 2>/dev/null ) &
+( curl -sS -N -m 60 -b "sess=$S" "$B/index_rd.php?stat=&subst=&step=-1&from=index_php&transp=xhr" > "$BRISK_WORK/ramp.stream" 2>/dev/null ) &
 sleep 3
-cd /root/load
-timeout 100 python3 brisk_load.py --auth --clients 150 --base 101 --silent --warmup 5 --duration 35 --port 8082 > /tmp/load5.log 2>&1
+timeout 100 python3 "$BENCH/brisk_load.py" --auth --clients 150 --base 101 \
+        --silent --warmup 5 --duration 35 --port 8082 > "$BRISK_WORK/load5.log" 2>&1
 wait
index 409f54d6f0aa22acb6c397030f15f45195448b8f..6fab590655979af3fc6f401264cf60171558427d 100755 (executable)
@@ -3,12 +3,16 @@
 # being with a real browser. They stay connected and keep their streams open,
 # so the table forms as soon as the human sits down.
 # Subprocess loops outlive their parent: without this they are left orphaned,
-# spinning for nothing (it happened: eighty loops alive for two days). kill 0
-# kills the process group, which setsid makes exclusive to this script.
-trap "kill 0" EXIT INT TERM
+# spinning for nothing (it happened: eighty loops alive for two days).
+#
+# bench_reap walks our descendants, so it reaches them without touching the
+# caller or the other half of a pipeline. See common.sh.
+. "$(dirname "$0")/common.sh"
+trap bench_reap EXIT INT TERM
 
-B="https://127.0.0.1:8444/brisk"; TAB="${1:-4}"; cd /tmp
+B="https://127.0.0.1:8444/brisk"; TAB="${1:-4}"
 CURL="curl -sSk"
+cd "$BRISK_WORK"
 rm -f auth.txt r?.stream k?.stream mani.txt tok.txt
 
 for u in load001 load002 load003 load004; do
index 85a6e3c8ec6c2c0a35a02d022124fb40b7f0d1d2..f957a6d219e44da5522abf67a61bbb9b63e47f12 100755 (executable)
@@ -6,6 +6,8 @@
 # this very script, which would kill itself and leave the targets alive. That
 # happened several times, and it is how eighty loops survived for two days.
 
+. "$(dirname "$0")/common.sh"
+
 MIO=$$
 PAT="game\\.sh|hand\\.sh|game_pkg|game_https|game_t5|game_t9|hand_pkg|hand_https|sit4|play_human"
 PAT="$PAT|join4|gjoin|ramp_probe|brisk_load|slowprobe|wsprobe|takeover"
@@ -24,8 +26,11 @@ for p in $(pgrep -x curl); do kill -9 "$p" 2>/dev/null; done
 sleep 2
 
 # The capture files grow without bound: two days of runaway loops had filled
-# the disk, which is the same one as the host's.
-rm -f /tmp/*.stream /tmp/*.html 2>/dev/null
+# the disk, which is the same one as the host's. The streams grow the whole
+# time they are open, so they are the ones that hurt.
+rm -f "$BRISK_WORK"/*.stream "$BRISK_WORK"/*.html "$BRISK_WORK"/*.log \
+      "$BRISK_WORK"/*.out "$BRISK_WORK"/auth.txt "$BRISK_WORK"/tok.txt \
+      "$BRISK_WORK"/mani.txt 2>/dev/null
 
 d="$(pgrep -f 'php \./brisk-spush\.php' | head -1)"
 echo
@@ -33,8 +38,10 @@ echo "=== final state ==="
 printf "  test scripts running:   %s\n" "$(pidlist | grep -c .)"
 printf "  curl running:           %s\n" "$(pgrep -x curl | wc -l)"
 printf "  daemon:                 %s process, %s descriptors (10 are the listening sockets)\n" \
-       "$(pgrep -f 'php \./brisk-spush\.php' | wc -l)" "$(ls /proc/$d/fd 2>/dev/null | wc -l)"
+       "$(pgrep -f 'php \./brisk-spush\.php' | wc -l)" "$($SUDO ls /proc/$d/fd 2>/dev/null | wc -l)"
 printf "  disk space:             %s\n" \
        "$(df -h / | awk 'NR==2 { print $4 " free (" $5 " used)" }')"
-printf "  daemon log:             %s\n" "$(du -sh /tmp/brisk.log 2>/dev/null | cut -f1)"
+printf "  daemon log:             %s\n" "$(du -sh "$BRISK_LEGAL/brisk.log" 2>/dev/null | cut -f1)"
+printf "  working files:          %s in %s\n" \
+       "$(ls -1 "$BRISK_WORK" 2>/dev/null | wc -l)" "$BRISK_WORK"
 printf "  load:                   %s\n" "$(uptime | sed 's/.*average: //')"