Skip to content

fix(distributed): one bad worker result no longer loses the whole run - #288

Merged
kurok merged 1 commit into
mainfrom
fix/master-survives-bad-worker
Sep 19, 2026
Merged

kurok merged 1 commit into
mainfrom
fix/master-survives-bad-worker

Conversation

@kurok

@kurok kurok commented Sep 19, 2026 •

Copy link
Copy Markdown
Owner

Closes #237

The bug

_collect_one caught TimeoutError / ConnectionError / OSError, but _deserialize_stats raises ValueError, AttributeError or KeyError on any non-conforming JSON — a status-code key that is not a number, a list where an object belongs, a missing "stats".

So one buggy worker turned the entire cluster run into a traceback, taking every other node's results with it. And since the control plane is unauthenticated unless --worker-secret is set, the payload need not even come from a worker anyone trusts.

Reproduced: _deserialize_stats({"status_codes": {"abc": 1}}) → ValueError: invalid literal for int() with base 10: 'abc', propagating straight out of run_master.

The fix

The deserialize is wrapped. A malformed result is logged against the worker that sent it and discarded; every other node's results are still merged and reported.

_deserialize_stats now validates the shapes it reads rather than trusting them, and every rejection names the field. That matters because the message is what the master logs, per worker:

status_codes key 'abc' is not a status code: invalid literal for int() with base 10: 'abc'

reads better in an operator's log than the bare invalid literal did. latencies must be an array, got str, rps_timeline entry must be an array, got int, and so on. True is rejected for a numeric field too — isinstance(True, int) is True in Python, and a bool in total_requests is a bug worth surfacing.

Tests

  • tests/test_audit_distributed.py::TestMasterSurvivesBadWorker::test_master_survives_malformed_result_from_one_worker — two real sockets, one valid result of 250 requests and one {"status_codes": {"abc": 1}}. Asserts merged.total_requests == 250 and that the bad worker was reported. Pre-fix: ValueError: invalid literal for int() with base 10: 'abc' out of run_master, no result at all.
  • tests/test_distributed.py::TestMalformedStats — seven malformed shapes as subtests, each asserting the message names its field, plus a round-trip test so the hardening did not break the valid path. All seven subtests fail pre-fix.

Checks

ruff format/check clean, mypy clean, 1913 passed.

_collect_one caught TimeoutError/ConnectionError/OSError, but
_deserialize_stats raises ValueError, AttributeError or KeyError on any
non-conforming JSON -- a status-code key that is not a number, a list
where an object belongs, a missing "stats". One buggy worker turned the
entire cluster run into a traceback, and since the control plane is
unauthenticated unless --worker-secret is set, the payload need not even
come from a worker anyone trusts.

The deserialize is now wrapped: a malformed result is logged against the
worker that sent it and discarded, and every other node's results are
still merged and reported.

_deserialize_stats validates the shapes it reads rather than trusting
them, and each rejection names the field -- the message is what the
master logs, so "status_codes key 'abc' is not a status code" beats
"invalid literal for int() with base 10: 'abc'".

Closes #237

Signed-off-by: Yurii Riabikov <22548029+kurok@users.noreply.github.com>

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@kurok
kurok merged commit 1e7c55d into main Sep 19, 2026
18 checks passed
@kurok
kurok deleted the fix/master-survives-bad-worker branch September 19, 2026 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

reliability(distributed): master crashes on one malformed worker result, losing all other results

1 participant