Conversation
The held-value window is clamp(3 x instance_tick, 6s, 30s), which at the default 2s cadence clamps to the 6s floor. After the scenario flipped the mock to failure and waited for one 503 to land, barely 4s of that window remained before the assertion read the screen. Under CI runner contention the scrape, snapshot and render all slip past that, the held value expires on schedule, and the assertion fails on correct behaviour. Pin the scenario's scrape cadence to 10s via the ordinary dashboard.daemon.instance_tick_secs config field, which saturates the 30s ceiling and leaves roughly 20s of margin instead of 4s. No production change and no test-only seam. Drop the held assertion from dash-09: it duplicated dash-08 verbatim and was the only reason dash-09 inherited the same race. The scenario still tells the whole story, because it asserts throughput is visible before the failure. Poll for expiry rather than sleeping a fixed span -- that direction cannot race, since extra delay only makes the value more expired. Also corrects both scenarios' comments, which still described the fix as unimplemented and the assertions as expected to fail. Closes #379 Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
|
Superseded by #380, which landed the same fix about half an hour before I opened this — my mistake for not re-checking #379 immediately before opening rather than only at the start of the work. #380's logical-clock seam is the stronger fix for the hold boundary: it cannot be crossed by host scheduling at all, whereas this PR only widened the margin from ~4 s to ~20 s through Nothing here is worth salvaging into a follow-up. The one durable artifact — how to actually reproduce the failure, which turns on the suite's own |
Problem
E2E testsis a required check and has been failing ondash-08/dash-09since #329 landed. The timeline points at scenario count rather than at any one change:E2E testsrun that completed before that (85–88 scenarios) passed.The production code is fine. These scenarios assert correct behaviour and then lose a race against it.
Root cause
The held-value window is
clamp(3 × instance_tick, 6 s, 30 s), which at the default 2 s cadence clamps to the 6 s floor. Expiry is measured from the last successful observation (GenerationObservationTracker::snapshot).The scenario flips the mock to failure, polls until one 503 has been served, then reads the screen. By then the last good observation is already up to one cadence old, so only ~4 s of the window is left.
The contention that eats those 4 s is not ambient CPU load — it is the suite itself. The no-GPU lane runs
max_concurrent_scenarios(64)(tests/e2e-cucumber/tests/e2e.rs), so up to 64 scenarios, each with a PTY, a daemon and a mock server, run at once on a small runner. The daemon's ticker isMissedTickBehavior::Skip, so under starvation it drops scrapes and the last good observation ages out. More scenarios in the binary means more pressure, which is why the failure appeared exactly when the count grew.Fix
Pin the scenario's scrape cadence to 10 s through
dashboard.daemon.instance_tick_secs— the ordinary config field, written into the scenario's already-isolated config dir. That saturates the 30 s ceiling and leaves roughly 20 s of margin instead of 4 s. No production change and no test-only seam.Two smaller changes:
Both scenarios' comments still claimed the fix was unimplemented and the assertions were expected to fail (
runner.rshas heldgen_trackersacross failures since EAI-7960 landed). Corrected.Verification
The failure reproduces locally by recreating the real condition — the full suite at concurrency 64, pinned to 2 cores under load. Two scenarios in isolation never reproduce it, however much CPU load is applied; the pressure has to come from the other scenarios.
dash-08,dash-09, same message as CIAlso confirmed the config genuinely reaches the daemon rather than the test passing for the old reasons:
dash-08takes 30 s with it and 15 s without, which is the cadence change showing up in the scrape schedule.cargo clippy --locked --workspace --all-targets -D warnings,cargo clippy --locked -p e2e-cucumber --test e2e -D warningsandcargo fmt --checkare clean.rocm-dash-core's 39 injected-time observation tests are untouched — the boundary arithmetic was always covered deterministically there; what these scenarios add is the wiring (tracker → snapshot → rendered screen), which has no coverage at the runner level.Tradeoffs
dash-08grows to roughly 25 s anddash-09to roughly 45 s. Accepted: it is mostly sleeping rather than CPU, against a permanently red required check.runner.rscallsUtc::now()directly), which is the test-backdoor pattern Remove the e2e-test-hooks feature #349 exists to delete.MAX_VALIDITYcaps what config can buy. If it ever flakes again, the fallback is to drop these two from E2E and add runner-level wiring coverage instead.Closes #379