Skip to content

[master] Fix timing-fragile unit + stress tests exposed by container-image drift - #70230

Open
dwoz wants to merge 2 commits into
saltstack:masterfrom
dwoz:dwoz/fix/master-mf-failures
Open

[master] Fix timing-fragile unit + stress tests exposed by container-image drift#70230
dwoz wants to merge 2 commits into
saltstack:masterfrom
dwoz:dwoz/fix/master-mf-failures

Conversation

@dwoz

@dwoz dwoz commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes two test-failure clusters on master exposed by the 2026-08-31 salt-ci-containers image rebuild (see agents/reports/rootcause-pr-vs-push-divergence for the digest evidence -- pre/post-rebuild image digests differ, same salt tree hash, same runner).

  • tests/pytests/unit/test_minion.py::test_send_req_async_regression_62453 -- teardown ERROR from the _asyncio_blocking_detection autouse fixture: start_engines() runs sync at bootstrap and its 50-115 ms cost tips past the 50 ms budget on the new container. Added @pytest.mark.no_blocking(reason=...) -- mirrors the round-2 exemption pattern (PR Async MWorker: convert AESFuncs/ClearFuncs handlers to async dispatch #70129). Systemic follow-up (refactor start_engines() to yield, or narrow the fixture's scope) noted but out of scope.

  • tests/pytests/stress/master_subprocess/mworker/test_mworker_stress.py::test_requester_disconnect_midflight_leaves_worker_alive -- round-2's single-shot 2 s stale-reply drain misses the queued drop-me reply on the new container (arrives >2 s later); replaced with a loop-until-matching-reply within a 15 s deadline.

  • tests/pytests/stress/master_subprocess/mworkerqueue/test_mworkerqueue_stress.py::test_requester_churn_fd_bounded -- the first churn cycle races the worker's initial DEALER<->REP handshake to the queue subprocess; added an explicit warmup round-trip with a 10 s poll budget before churn starts, and widened per-cycle poll from 2 s to 5 s. MWorkerQueueHandle is a proxy-shape handle, not a subprocess-DEALER-bind like MWorkerHandle, so the round-2 DEALER-context-swap pattern doesn't apply -- the actual failure is a startup race, addressed accordingly.

Fixes CI failures on master run 33438729348.

Test plan

  • venv314/bin/pytest on all three affected tests locally (3x back-to-back stability check on the stress pair, all green)
  • CI (test:full label applied)

Sibling 3008.x PR

Same tests are also red on 3008.x since 2026-08-31 (verified in the prior investigation report). A sibling PR against 3008.x should mechanically port these commits, otherwise the next 3008.x -> master merge-forward will re-conflict.

dwoz added 2 commits September 3, 2026 03:38
…ection

The Minion.__init__() path exercised by this test synchronously executes
the engine-bootstrap Handle in salt/engines/__init__.py:16 (a
loop.call_soon that runs start_engines() inline). The 50-115 ms cost is
an inherent characteristic of the sync bootstrap path, not a handler
regression: on the 2026-08-31 salt-ci-containers image rebuild the
runtime tipped just past the fixture's 50 ms budget and the test began
ERROR'ing at teardown.

Mirrors the round-2 exemption pattern applied to
test_asynchronous.py::{test_helpers, test_double, test_double_sameloop}
(PR saltstack#70129) and test_master.py::test_register_resources_* (in-tree).
Refactoring start_engines() to yield mid-work is the systemic fix; the
tag is the minimum viable patch.
Two failures on master run 33438729348 traced back to the 2026-08-31
salt-ci-containers image rebuild widening zmq handshake / reply-flush
windows past hardcoded per-op poll budgets:

* mworker::test_requester_disconnect_midflight_leaves_worker_alive
  Round-2 (saltstack#70129) added a stale-reply drain step (single 2 s recv)
  before send_recv("after-reconnect"). On the new container the queued
  drop-me reply consistently arrives >2 s post-reconnect and slips past
  the bounded drain, so send_recv returns the stale reply and the
  assertion flips (drop-me != after-reconnect). Replace the bounded
  drain with a loop-until-matching-reply within a 15 s deadline: any
  stale queued reply is logged and discarded until we see the fresh
  "after-reconnect" one.

* mworkerqueue::test_requester_churn_fd_bounded
  The very first churn cycle (warm0) races the worker's initial
  DEALER<->REP handshake to the queue subprocess. On the new container
  the handshake regularly exceeds the test's 2 s per-cycle poll,
  producing "queue stalled at warm0". Add an explicit warmup round-trip
  after mworkerqueue.worker() with a 10 s poll budget so the churn
  budget starts against an already-attached worker; also widen the
  per-cycle poll from 2 s to 5 s for headroom.

MWorkerQueueHandle shape does not match MWorkerHandle (proxy vs
subprocess-DEALER-bind); the round-2 DEALER context-swap pattern does
not apply here. The mworkerqueue churn test opens fresh REQ sockets per
iteration, so the actual failure mode is a startup race, not a rebind
race -- addressed accordingly.
@dwoz
dwoz requested a review from a team as a code owner September 3, 2026 10:39
@dwoz dwoz added the test:full Run the full test suite label Sep 3, 2026
@dwoz dwoz added this to the Potassium v3009.0 milestone Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:full Run the full test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants