test(job-array-single-wal-record): large-array HA soak scenario

Extends the HA soak harness with a new --array-soak mode: small (N=2)
and large (N=10000) array submission sub-rounds, each with one clean
kill/failover, asserting one WAL record per submission regardless of
array size, per-element addressability (status/cancel/completion)
spread across low/mid/high indices post-failover, and a real
millisecond-resolution replay-time bound comparing large-array vs
small-array kill-to-addressable latency.

waldump.c gains a WAL_JOB_ARRAY_SUBMIT case in wal_type_name() so the
scenario can grep an exact type name for its live WAL-record-count
delta check.

Went through one remediation round: the replay-time measurement
initially used whole-second resolution (collapsed to 0s/n-a on a real
run) and never asserted on the comparison, giving zero actual evidence
for the O(1)-replay-time requirement. Fixed with $EPOCHREALTIME
(millisecond resolution -- this sandbox's BusyBox date silently
mangles the more common `date +%s%3N` idiom) and a real bound
assertion that hard-fails the run on a genuine O(N) regression,
independently verified to have real teeth.

Live-verified 3 times with genuinely discriminating numbers each time;
final run: small=210ms, large=163ms -- large-array replay was not
slower, live evidence of the feature's O(1) claim.

Task 6.1 of job-array-single-wal-record (Requirements 4.3, 5.2, 5.3,
8.2, 8.3). Depends on and reuses task 6.0a's own soak verification.
This commit is contained in:
Rongsong Shen 2026-07-16 04:43:55 +00:00
parent 16747aa96d
commit 0d8986a6a9
6 changed files with 550 additions and 2 deletions

View file

@ -28,6 +28,12 @@ static const char* wal_type_name(uint32_t t)
{
switch (t) {
case WAL_JOB_SUBMIT: return "JOB_SUBMIT";
case WAL_JOB_ARRAY_SUBMIT: return "JOB_ARRAY_SUBMIT"; /* job-array-single-
* wal-record task 6.1: name the type so the HA soak's WAL-record-count
* assertion (exactly one JOB_ARRAY_SUBMIT per array submission,
* regardless of array size) can grep an exact type name instead of
* the generic "UNKNOWN" bucket, which would also catch any future,
* genuinely-unrecognized type. */
case WAL_JOB_START: return "JOB_START";
case WAL_JOB_FINISH: return "JOB_FINISH";
case WAL_JOB_FAIL: return "JOB_FAIL";