Files
MARTe-Integrated-Components/docs/superpowers/specs/2026-06-26-stress-suite-report-integration-design.md
Martino Ferrari 0d7d8f396b docs(e2e-stress): design for stress suite report integration
Track the existing (untracked) stress matrix, extend the signal-size axis
into the multi-fragment regime now that the UDPSClient reassembly cap is
1 MiB, and wire stress results into the E2E PDF report via an opt-in
--stress flag (table + per-axis scaling curves + regression vs prior run).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-26 09:05:21 +02:00

6.2 KiB
Raw Permalink Blame History

Stress Suite Report Integration — Design

Date: 2026-06-26

Context

A capacity/stress suite already exists in Test/E2E/chain/ but is untracked in git and not wired into the report:

  • stress.py — declarative matrix, 27 cases across 7 load axes, all valid.
  • stress_run.py — orchestrator: runs each case, captures cpu/peakRSS via proc_perf.snapshot(), zoom p50/p95 latency, gates survival+liveness (hard) and RSS/zoom-p95 (soft), writes stress_results.json.
  • run_stress.sh — standalone wrapper (build + env + invoke stress_run.py).
  • client/main.gostress mode (liveness + zoom-latency recorder).

The suite is functional (smoke-tested ds_size_1000 → PASS end-to-end). The report pipeline (run_chain_e2e.sh, collect.py, report_build.py, E2E_Report.typ) has zero stress references.

This work covers the user's three requested stress axes — signal size, client count, source count — which already map onto existing matrix axes ({ds,hub}_signal_elements, hub_ws_clients / ds_subscriber_hubs, hub_source_count), plus the report integration.

Separately, the UDPSClient reassembly cap was just raised from 64 KB to 1 MiB (UDPS_CLIENT_MAX_PACKET_BYTES = 1048576), with exact assembledBytes delivery. The stress matrix still enforces a now-outdated sub-64 KB single-datagram ceiling.

Goals

  1. Extend the signal-size axis into the multi-fragment regime (exercise the fixed reassembly path) and lift the outdated 64 KB validation cap.
  2. Run the stress suite as an opt-in phase of run_chain_e2e.sh (--stress).
  3. Surface stress results in the PDF: a table, per-axis scaling curves, and progression/regression vs the previous run (tracked in history.jsonl).

Non-goals: waveform fidelity under stress (UDP loss is expected; the hub decimates to PushRate), and changing the stress harness's gating semantics.

Design

1. Matrix extension (stress.py)

  • Extend _DS_SIZE and _HUB_SIZE with multi-fragment cases: add 50000, 100000, 250000 elements (≈200 KB, 400 KB, ~1 MB float32 packets).
  • Large packets get a slower producer to keep bandwidth realistic (mirroring s51's 100 Hz / 10 ms cycle): set producer_hz/row_dt per case so a ~1 MB packet is not emitted at 1 kHz (= 1 GB/s). sampling_rate stays elements / row_dt so the FirstSample per-cycle window equals one producer cycle (keeps the hub ring's time axis monotonic — same constraint scenarios.py enforces).
  • Lift the single-datagram validation in validate_case() from 64 KB to the 1 MiB deliverable cap (UDPS_CLIENT_MAX_PACKET_BYTES = 1048576). Update the module docstring (lines 4447) to reflect multi-fragment support.

2. Report data wiring (run_chain_e2e.sh, report_build.py)

  • run_chain_e2e.sh gains --stress (default off). When set, after the correctness phase and before collect.py/report_build.py, it runs the stress phase, reusing run_stress.sh's env/LD_LIBRARY_PATH setup and the stress_run.py invocation, producing stress_results.json in Build/x86-linux/E2E/chain/stress/. Honors --skip-build.
  • report_build.py reads stress_results.json if present and folds a stress block into report_data.json:
    • stress.overall, stress.cases[] (id, axis, level, status, survival, marte_cpu_s/rss, hub_cpu_s/rss, zoom p50/p95, fails).
    • stress.by_axis — cases grouped by axis, sorted by level (for the curves).
  • When stress was not run, report_data.json has no stress block and the PDF omits the section (no stale data).

3. PDF section + scaling plots (report_build.py, E2E_Report.typ)

  • New = Stress Tests section in E2E_Report.typ, after the Performance section.
  • Table — one row per case: axis, level, status, marteCPU, marteRSS, hubRSS, zoom p50/p95 (matching the existing Performance table style).
  • Scaling curvesreport_build.py generates one PNG per axis (reusing its matplotlib trend-plot helper): load level (x) vs the relevant metric(s) — RSS & CPU vs level for size/count/sources, zoom-p95 vs reqrate, frames/RSS vs ws_clients. PNGs land alongside the existing trend plots and are embedded in the Typst section.

4. Regression + history (report_build.py)

  • Append a stress summary to each history.jsonl run record: per-case key metrics (marte/hub RSS & CPU, zoom-p95, min_frames).
  • Extend the _DIRECTION map: RSS/CPU/zoom-p95 → lower-is-better; min_frames → higher-is-better. Surface stress regressions in the existing headline/delta mechanism (▲/▼ vs last run), consistent with E2E metrics.
  • First-run guard: when no prior stress history exists, show baseline values with no delta (existing is_first_run handling).

5. --stress flag plumbing (run_chain_e2e.sh)

  • Add --stress to the arg parser; default off (correctness-only stays the fast default).
  • Update the run_chain_e2e.sh header comment and the CLAUDE.md E2E paragraph to document --stress.

Full flow

run_chain_e2e.sh [--stress]
  ├─ correctness phase → results.json + perf JSON   (unchanged)
  ├─ [if --stress] stress phase → stress_results.json
  ├─ collect.py        → unit tests + coverage        (unchanged)
  └─ report_build.py   → report_data.json (+stress block, +history/regression)
        └─ E2E_Report.typ → PDF (Stress Tests: table + per-axis scaling curves)

Verification

  • python3 stress.py validates the extended matrix (multi-fragment cases pass the lifted cap).
  • A multi-fragment size case (e.g. ds_size_100000) runs PASS via run_stress.sh --only ds_size_100000 (exercises reassembly).
  • run_chain_e2e.sh --stress --skip-build produces a PDF with the Stress Tests section (table + curves); a run without --stress omits it.
  • Second --stress run shows progression/regression deltas vs the first.

Risks

  • Bandwidth at large element counts — mitigated by the slower producer; verify the hub keeps up (survival gate) and doesn't OOM (RSS gate; ~1 MB/slot ×4 slots ×N sources).
  • Runtime — 27+ cases at ~68 s each adds minutes; mitigated by opt-in --stress.
  • history.jsonl already polluted by prior single-scenario runs — stress history starts fresh (new key); no rewrite of existing E2E history.