@@ -80,7 +80,21 @@ STREAM_DURATION=120
8080# of do_hup_case(), so these defaults cannot change any other case.
8181PERF_THREADS=1
8282PERF_CONNS=12
83- PERF_DURATION=110
83+ # The probe has to outlive the reload window: the worst observed window is
84+ # ~120 s (a stalled drain hits the 90 s deadline and G_old then quits), and
85+ # 'probe still running after the reload' is part of the verdict. Note that
86+ # this also widens that liveness margin (~105 s -> ~175 s); every rate is
87+ # n / PERF_DURATION, so baselines stay comparable, and the longer window is
88+ # stricter for the fail=0 style criteria.
89+ PERF_DURATION=180
90+ # HUP anchor: the reload is sent once the probe has proven this much
91+ # progress, instead of a fixed sleep after the process merely exists.
92+ # Measured: at 3 s the probe has already produced ~20k requests (proof of
93+ # active traffic) and the window drains in ~2 s; anchoring later (8 s,
94+ # ~55k requests) reproducibly strands a half-open entry in G_old and the
95+ # drain runs to its 90 s deadline (2/2 runs), so 3 s is the default.
96+ HUP_ANCHOR_SEC=3
97+ HUP_ANCHOR_COUNT=200
8498KERNEL_NIC_IP=" "
8599# Runtime fault injection (FF_FAULT). Empty = production form; a non-empty name
86100# requires a fault-injection build manifest (checked by reload_checks.verify-build).
@@ -108,6 +122,7 @@ EXCLUDED_CASES="rt13"
108122ERRLOG=" "
109123PIDFILE=" "
110124STREAM_MD5=" "
125+ HUP_ANCHOR_LINE=" "
111126HUP_SUMMARY=" NA"
112127HUP_DRAIN=" NA"
113128HUP_FWD=" NA"
@@ -196,7 +211,9 @@ Usage: test_graceful_reload.sh -t <TARGET_IP> [options]
196211 --rte-fresh-min <min> /var/run/dpdk/rte freshness gate (default 10)
197212 --perf-threads <n> m4_cps.py threads for rt30 (default 1)
198213 --perf-conns <n> m4_lc.py connections for rt31 (default 12)
199- --perf-duration <s> probe duration for rt30/rt31 (default 110)
214+ --perf-duration <s> probe duration for rt30/rt31 (default 180)
215+ --hup-anchor-sec <s> HUP once the probe ran this long (default 3)
216+ --hup-anchor-count <n> ... and produced this many requests (default 200)
200217
201218Exit: 0 all pass | 100+N N cases failed | 2 usage | 3 preconditions
202219 | 4 dependency missing | 5 aborted
@@ -244,6 +261,8 @@ while [ $# -gt 0 ]; do
244261 --perf-threads) PERF_THREADS=" ${2:- } " ; shift 2 ;;
245262 --perf-conns) PERF_CONNS=" ${2:- } " ; shift 2 ;;
246263 --perf-duration) PERF_DURATION=" ${2:- } " ; shift 2 ;;
264+ --hup-anchor-sec) HUP_ANCHOR_SEC=" ${2:- } " ; shift 2 ;;
265+ --hup-anchor-count) HUP_ANCHOR_COUNT=" ${2:- } " ; shift 2 ;;
247266 -h|--help) usage; exit 0 ;;
248267 * ) die_usage " unknown option: $1 " ;;
249268 esac
@@ -268,6 +287,7 @@ local -a values=()
268287for n in TARGET_IP CLIENT CASES ROUNDS INTERVAL POLL WORKERS DRAIN_TIMEOUT STARTUP_WAIT \
269288 STREAM_MB RTE_FRESH_MIN SHUTDOWN_TIMEOUT BASELINE_DURATION GRACEFUL ZC_BUILD FAULT \
270289 FAULT_DELAY_MS PERF_THREADS PERF_CONNS PERF_DURATION \
290+ HUP_ANCHOR_SEC HUP_ANCHOR_COUNT \
271291 NGINX_BIN FSTACK_TPL PROBE_DIR OUT BUILD_MANIFEST KERNEL_NIC_IP LCORE_MASK LCORE_LIST; do
272292 values+=(" $n =${! n} " )
273293done
@@ -830,6 +850,9 @@ hup_once() { # conf
830850# as a regression either, hence the dedicated code.
831851do_hup_case () { # tag graceful shutdown_timeout probe-kind(none|stream|lc|cps) [check-mode]
832852 local tag=" $1 " g=" $2 " st=" $3 " probe=" $4 " mode=" ${5:- } "
853+ # Reset the anchor: it is global so case_rt30/rt31 can quote it, and a
854+ # leftover from the previous case must never be reported as this one's.
855+ HUP_ANCHOR_LINE=" "
833856 local conf out rc=0 nodata=0 wave=0 fetch=0 summary=" no traffic probe"
834857 conf=$( gen_nginx_conf " $tag " " $st " )
835858 push_probes || return 1
@@ -860,11 +883,19 @@ do_hup_case() { # tag graceful shutdown_timeout probe-kind(none|stream|lc|cps) [
860883 lc)
861884 launch_probe " $tag " 240 m4_lc.py --server " $TARGET_IP " --conns " $PERF_CONNS " \
862885 --interval 0.1 --duration " $PERF_DURATION " --fresh 0.5 --timeout 2 || return 1
863- sleep 3 ;;
886+ HUP_ANCHOR_LINE=$( wait_probe_progress LC_PROGRESS " $HUP_ANCHOR_SEC " \
887+ " $HUP_ANCHOR_COUNT " 60) \
888+ || { say " $tag : lc probe never reached the HUP anchor "
889+ " (${HUP_ANCHOR_SEC} s / ${HUP_ANCHOR_COUNT} requests)" ; return 1; }
890+ say " $tag : HUP anchored at: $HUP_ANCHOR_LINE " ;;
864891 cps)
865892 launch_probe " $tag " 240 m4_cps.py --server " $TARGET_IP " --threads " $PERF_THREADS " \
866893 --duration " $PERF_DURATION " --timeout 2 || return 1
867- sleep 3 ;;
894+ HUP_ANCHOR_LINE=$( wait_probe_progress CPS_PROGRESS " $HUP_ANCHOR_SEC " \
895+ " $HUP_ANCHOR_COUNT " 60) \
896+ || { say " $tag : cps probe never reached the HUP anchor "
897+ " (${HUP_ANCHOR_SEC} s / ${HUP_ANCHOR_COUNT} requests)" ; return 1; }
898+ say " $tag : HUP anchored at: $HUP_ANCHOR_LINE " ;;
868899 esac
869900
870901 # Only the perf cases sample process CPU: the inventory walk would
@@ -875,7 +906,10 @@ do_hup_case() { # tag graceful shutdown_timeout probe-kind(none|stream|lc|cps) [
875906 esac
876907 [ " $probe " = none ] || probe_running || { say " $tag : probe not running before the reload" ; rc=1; }
877908 hup_once " $conf " || rc=1
878- [ " $probe " = none ] || probe_running || { say " $tag : probe not running after the reload (window longer than the probe?)" ; rc=1; }
909+ [ " $probe " = none ] || probe_running \
910+ || { say " $tag : probe not running after the reload (window longer than "
911+ " the probe? drain=${HUP_DRAIN} ms duration=${PERF_DURATION} s "
912+ " anchor=${HUP_ANCHOR_LINE:- none} )" ; rc=1; }
879913
880914 # wait_client_summary reports 1 = no data (timeout / summary absent) and
881915 # 2 = the probe reported a summary but failed its own criterion. Only the
@@ -956,7 +990,7 @@ do_hup_case() { # tag graceful shutdown_timeout probe-kind(none|stream|lc|cps) [
956990# (est_pps), not measured on the wire.
957991case_rt30 () {
958992 say " === case rt30 (PT-NR-01: high CPS short connections) ==="
959- local hrc=0 rc=0 n rate cpu_per_1k est_pps crit meas
993+ local hrc=0 rc=0 n rate cpu_per_1k est_pps crit meas anchor remaining
960994 crit=" high-rate CPS: client fail=0 + reload complete + 6/6 FSM + workers back to $WORKERS "
961995 do_hup_case " rt30" 1 0 cps || hrc=$?
962996 if [ " $hrc " = " 2" ]; then
@@ -968,7 +1002,13 @@ case_rt30() {
9681002 rate=$( awk -v n=" ${n:- 0} " -v d=" $PERF_DURATION " ' BEGIN { if (n == 0 || d == 0) print "NA"; else printf "%.1f", n / d }' )
9691003 cpu_per_1k=$( awk -v c=" $HUP_CPU_MS " -v n=" ${n:- 0} " ' BEGIN { if (c == "NA" || n == 0) print "NA"; else printf "%.2f", c * 1000 / n }' )
9701004 est_pps=$( awk -v r=" $rate " ' BEGIN { if (r == "NA") print "NA"; else printf "%.0f", r * 9 }' )
971- meas=" mode=cps threads=$PERF_THREADS duration=${PERF_DURATION} s n=${n:- NA} rate=${rate} /s cpu_ms=$HUP_CPU_MS cpu_per_1k=$cpu_per_1k est_pps=$est_pps drain=${HUP_DRAIN} ms fwd=${HUP_FWD} rel=${HUP_REL} $HUP_SUMMARY "
1005+ # Anchor evidence: where in the probe life the reload was sent, and how
1006+ # much of the probe was left at that moment (not after the reload).
1007+ anchor=$( printf ' %s' " $HUP_ANCHOR_LINE " | sed -n ' s/.*elapsed=\([0-9.]*\).*/\1/p' )
1008+ [ -n " $anchor " ] || anchor=" NA"
1009+ remaining=$( awk -v d=" $PERF_DURATION " -v a=" $anchor " \
1010+ ' BEGIN { if (a == "NA") print "NA"; else printf "%.1f", d - a }' )
1011+ meas=" mode=cps threads=$PERF_THREADS duration=${PERF_DURATION} s n=${n:- NA} rate=${rate} /s cpu_ms=$HUP_CPU_MS cpu_per_1k=$cpu_per_1k est_pps=$est_pps drain=${HUP_DRAIN} ms fwd=${HUP_FWD} rel=${HUP_REL} anchor=${anchor} s remaining=${remaining} s $HUP_SUMMARY "
9721012 if [ " $rc " = " 0" ]; then
9731013 record " rt30" " PASS" " $crit " " $meas "
9741014 else
@@ -979,7 +1019,7 @@ case_rt30() {
9791019
9801020case_rt31 () {
9811021 say " === case rt31 (PT-NR-02: high packet rate, long connections) ==="
982- local hrc=0 rc=0 n rate cpu_per_1k est_pps crit meas
1022+ local hrc=0 rc=0 n rate cpu_per_1k est_pps crit meas anchor remaining
9831023 crit=" high packet rate: fresh_fail=0 + at most one keep-alive closure per connection (G_old drains) + reload complete + 6/6 FSM + workers back to $WORKERS "
9841024 do_hup_case " rt31" 1 0 lc perf || hrc=$?
9851025 if [ " $hrc " = " 2" ]; then
@@ -991,7 +1031,13 @@ case_rt31() {
9911031 rate=$( awk -v n=" ${n:- 0} " -v d=" $PERF_DURATION " ' BEGIN { if (n == 0 || d == 0) print "NA"; else printf "%.1f", n / d }' )
9921032 cpu_per_1k=$( awk -v c=" $HUP_CPU_MS " -v n=" ${n:- 0} " ' BEGIN { if (c == "NA" || n == 0) print "NA"; else printf "%.2f", c * 1000 / n }' )
9931033 est_pps=$( awk -v r=" $rate " ' BEGIN { if (r == "NA") print "NA"; else printf "%.0f", r * 3 }' )
994- meas=" mode=lc conns=$PERF_CONNS duration=${PERF_DURATION} s reqs=${n:- NA} rate=${rate} /s cpu_ms=$HUP_CPU_MS cpu_per_1k=$cpu_per_1k est_pps=$est_pps drain=${HUP_DRAIN} ms fwd=${HUP_FWD} rel=${HUP_REL} $HUP_SUMMARY "
1034+ # Anchor evidence: where in the probe life the reload was sent, and how
1035+ # much of the probe was left at that moment (not after the reload).
1036+ anchor=$( printf ' %s' " $HUP_ANCHOR_LINE " | sed -n ' s/.*elapsed=\([0-9.]*\).*/\1/p' )
1037+ [ -n " $anchor " ] || anchor=" NA"
1038+ remaining=$( awk -v d=" $PERF_DURATION " -v a=" $anchor " \
1039+ ' BEGIN { if (a == "NA") print "NA"; else printf "%.1f", d - a }' )
1040+ meas=" mode=lc conns=$PERF_CONNS duration=${PERF_DURATION} s reqs=${n:- NA} rate=${rate} /s cpu_ms=$HUP_CPU_MS cpu_per_1k=$cpu_per_1k est_pps=$est_pps drain=${HUP_DRAIN} ms fwd=${HUP_FWD} rel=${HUP_REL} anchor=${anchor} s remaining=${remaining} s $HUP_SUMMARY "
9951041 if [ " $rc " = " 0" ]; then
9961042 record " rt31" " PASS" " $crit " " $meas "
9971043 else
0 commit comments