Repository navigation
fix(test): retry a starved heartbeat window instead of failing or skipping it (#15266) - #15279
Conversation
…pping it (#15266) _MIN_TICKS_PER_WINDOW rejected a measurement window too short to hold a median, but did so by failing outright -- reporting a contended CI runner as a code regression, the same confusion #15221 was filed about. Add MeasurementStarved and measure_with_starvation_retry to perf_work_budget.py so a starved window is retried a small bounded number of times before it fails, while a completed measurement's own ratio assertion is never retried or masked -- a genuine regression still fails on its first attempt.
✅ SSOT Configuration Compliance: Passing🎉 No new hardcoded values of either class — Known backlog in |
…ontention (#15266) A scan that stalls the loop so completely the busy window comes up near-empty was indistinguishable from an idle-window-starved runner, so it retried 3x before failing with a message that read like ordinary contention. Add _raise_if_a_window_starved: the idle windows bracket the scan, so a healthy idle window alongside a starved busy window pins the shortfall on the scan itself and raises a plain, non-retried AssertionError; only a shortfall with no such asymmetry (both windows short, or idle alone) still raises MeasurementStarved and retries. Corrects measure_with_starvation_retry's docstring, which had claimed a guarantee this caller was not upholding.
Fixed: total-blockage regressions no longer retried as contentionCommit
New tests pin the discrimination directly ( Extracted Pre-push hook ran pytest on both touched test files against this branch and |
Thinking Path
_MIN_TICKS_PER_WINDOW = 10inprocess_offload_test.pyexists to reject ameasurement window too short to hold a median — a sound precondition. But it
rejected by failing, which reports a contended CI runner as a code
regression: exactly the confusion #15221 was filed about, one layer down
(#15221's reopening comment traces this).
Both obvious branches are wrong. Skipping is how a real regression hides —
pytest.skipin this same file was already found unreachable once and fixedin #15248, and reintroducing a skip path would undo that. Failing calls a
busy runner a regression, which is the defect under repair. A measurement
that could not be taken is neither a pass nor a failure — it is a retry.
Retake the window a small bounded number of times, and fail only if every
attempt starves. That distinguishes "this runner was momentarily busy" from
"this event loop is persistently starved", and only the second deserves a red.
The retry is scoped as narrowly as possible: it wraps only the "could this
window be measured at all" precondition inside
_tick_latency_during_scan,never the ratio assertions in the test body. Those run outside the retry
call entirely, so a genuine regression is scored on its first and only
attempt, exactly as before.
What Changed
autobot_shared/perf_work_budget.py: addedMeasurementStarved(anException, deliberately not anAssertionError, so it can never beconfused with — or accidentally catch — a real ratio-budget failure) and
measure_with_starvation_retry(measure, *, max_attempts, what), a generalasync retry helper for any contention site whose measurement window can
legitimately come up empty. It catches only
MeasurementStarved; onpersistent starvation it raises
AssertionErrorwith a message namingstarvation explicitly, distinct from a budget-breach message.
autobot-backend/code_intelligence/shared/process_offload_test.py:_tick_latency_during_scannow raisesMeasurementStarvedinstead ofasserting when a window under-fills. The test wraps each of its two
measurement calls (
force_thread=True/False) inmeasure_with_starvation_retrywith a new bounded constant,_MAX_STARVATION_RETRIES = 3. The test's own docstring states why this isa retry and not a skip or a pass, per test(perf): the process-offload heartbeat asserts a 7.5ms absolute ceiling, so runner load and a real regression are indistinguishable #15221's reopening comment.
autobot_shared/perf_work_budget_test.py: addedTestStarvationRetry,covering a clean first attempt (no retry), an eventually-successful retry,
persistent starvation reaching the bounded ceiling and still failing, and
— the mutation argument — a plain
AssertionError(what a completedmeasurement raises on a real regression, worded like the ~1.994x reading a
forced in-process scan produces against the 1.5 budget) propagating on
its first attempt, never retried or masked.
_TICK_BUDGET_VS_IDLE = 1.5is untouched._MIN_TICKS_PER_WINDOW = 10isuntouched — the precondition itself is correct; only what happens when it
isn't met has changed.
Verification
Read, not run, per the hard constraint (this is a timing test on the CI
runner itself):
_tick_latency_during_scanstill performs its cleanup(
shutdown_scan_pool,beat.cancel(), resetting_pool_unavailable_reason) before raisingMeasurementStarved, so eachretry attempt starts from clean state — unchanged from the prior
assert's position in the function.measure_with_starvation_retrycatches onlyMeasurementStarved. Theratio assertions (
in_process.busy_ms < in_thread.busy_ms * 0.75andassert_within_baseline_ratio(...)) execute in the test body, entirelyoutside any retry call — a regression there raises a plain
AssertionErroron the only attempt taken, with no retry loop in scope tocatch or repeat it.
1.994x its own idle baselineagainst the 1.5 budget, per test(perf): the process-offload heartbeat asserts a 7.5ms absolute ceiling, so runner load and a real regression are indistinguishable #15221'sclosure comment): that failure happens inside
assert_within_baseline_ratio,called directly in the test body — never wrapped by
measure_with_starvation_retry— so it still fails on first measurement,unaffected by this change.
TestStarvationRetry::test_a_measured_regression_is_never_retried_or_maskedencodes this directly with a synthetic
AssertionErrorworded the same way.TestStarvationRetry::test_persistent_starvation_fails_after_the_bounded_ceilingproves a
measurethat always raisesMeasurementStarvedis calledexactly
max_attemptstimes and then raisesAssertionErrornaming"starved on all N attempts" — bounded, not silently swallowed, not a skip.
pyteston both touched test files against thisbranch and reported all relevant tests passing (see push output on this
branch).
ruff checkandblack --checkboth pass clean on all three touchedfiles; every touched file is well under the 600-line cap
(
perf_work_budget.py281,perf_work_budget_test.py143,process_offload_test.py423).Closes #15221 pending confirmation on the reopener's own criteria (a
deliberately-starved run reaching a verdict rather than erroring on the
precondition, and the retry-vs-skip-vs-pass distinction stated in the test
itself) — leaving that issue for the reopener to close by hand per repo
convention, since
Closeskeywords do not fire onDev_new_gui.Model Used
Claude Sonnet 5
Closes #15266
Refs #15221 — this is the fix that its reopening comment set as the condition for closing again.