Skip to content

validator: survive chain-endpoint rate limiting instead of crashing#298

Closed
Thykof wants to merge 2 commits into
mainfrom
handle-metagraph-sync-failures
Closed

validator: survive chain-endpoint rate limiting instead of crashing#298
Thykof wants to merge 2 commits into
mainfrom
handle-metagraph-sync-failures

Conversation

@Thykof

@Thykof Thykof commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

What

The public chain endpoints rate-limit the heavy metagraph runtime call per source IP — the websocket handshake itself is rejected with HTTP 429, which surfaces as InvalidStatus / MaxRetriesExceeded. Two sync paths had no protection against this, and both kill the process:

  1. BaseNeuron.__init__ — the first subtensor.metagraph(netuid) fetch. When several neurons (re)start at once behind one IP, the unretried call crashes the process; the synchronized container restarts then keep re-triggering the limit (restart loops skip any startup stagger). Now retried with jittered exponential backoff (tenacity, reraise=True, up to 10 attempts / 120 s max wait — same pattern as save_responses), so the burst is waited out in-process and callers desynchronize naturally.
  2. Validator.refresh_metagraph — the boot call in forward_validator and the scoring loop's call run outside the scheduler's try/except. A raising sync is now swallowed with a logged exception: the previous miner set stays in use, and since the timer isn't stamped, the next cycle retries. This mirrors the method's existing degenerate-empty-sync semantics.

Observed in production this weekend: a namespace-wide rollout put most validator pods into CrashLoopBackOff on boot 429s, while pods that got past init handled the same 429 gracefully mid-cycle.

Tests

tests/test_refresh_metagraph.py: a raising sync neither propagates nor stamps the retry timer and keeps the previous miner set; a successful sync stamps it.

🤖 Generated with Claude Code

Two unprotected chain-sync paths could kill the process when the
public endpoint rate-limits the heavy metagraph runtime call per IP
(websocket handshake rejected with HTTP 429):

- BaseNeuron.__init__'s first metagraph fetch — now retried with
  jittered exponential backoff (tenacity, same pattern as
  save_responses), so a boot-time burst is waited out in-process
  instead of crash-looping, which kept re-triggering the limit.
- Validator.refresh_metagraph — a raising sync is now swallowed:
  the previous miner set stays in use and the unstamped timer makes
  the next cycle retry. This also protects the scoring loop, which
  calls it outside any try/except.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread synth/base/neuron.py Outdated
@Thykof Thykof closed this Jul 13, 2026
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.

1 participant