Skip to content

Fix rest_tornado dropping events to concurrent websocket clients#69698

Open
ggiesen wants to merge 3 commits into
saltstack:3006.xfrom
ggiesen:fix-35798-saltnado-ws-race
Open

Fix rest_tornado dropping events to concurrent websocket clients#69698
ggiesen wants to merge 3 commits into
saltstack:3006.xfrom
ggiesen:fix-35798-saltnado-ws-race

Conversation

@ggiesen

@ggiesen ggiesen commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

EventListener._handle_event_socket_recv in salt/netapi/rest_tornado/saltnado.py iterated over the futures list while calling .remove() on that same list, so with multiple websocket clients waiting under one (tag, matcher) key every other future was skipped and each event reached only some clients. The fix iterates over a snapshot (list(futures)) so every matching future gets resolved. Verified via the shadowed interpreter that the added unit test fails on the unpatched code ([True, False, True, False] of four waiters) and passes with the fix (all four delivered); note pytest itself hangs at collection in this sandbox even for pre-existing tests, so verification was done by executing the test logic directly through the shadowed salt-venv python.

What issues does this PR fix or reference?

Fixes #35798

Previous Behavior

See #35798.

New Behavior

Fix rest_tornado dropping events to concurrent websocket clients. Validated by a unit test proven to fail on unpatched 3006.x and pass with the fix (confirmed by adversarial review).

Merge requirements satisfied?

  • Tests written/updated
  • Changelog

Commits signed with GPG?

No

EventListener._handle_event_socket_recv iterated over the futures list
while removing delivered futures from that same list, which skips every
other future. With multiple websocket clients registered under the same
(tag, matcher) key, each event only reached some of the waiting clients.

Iterate over a snapshot of the list so every matching future is resolved.

Fixes saltstack#35798
…delivery

The direct test subscribes multiple clients through get_event(request) with
the exact defaults the websocket handlers in saltnado_websockets.py pass
(no tag, no matcher, so tag="" with prefix_matcher) and asserts a single
event resolves every waiting future; it fails without the snapshot fix in
_handle_event_socket_recv. The inverse test guards against overcorrection:
an already-done future must keep its original result and stay in the
tag_map, and a future waiting on a different exact tag (the
SaltAPIHandler.get_minion_returns shape) must stay pending; it passes with
and without the fix.
@dwoz dwoz added the test:full Run the full test suite label Jul 7, 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