Skip to content

feat(anomaly): detect replica full-resync failure loop (valkey#1836) - #373

Merged
jamby77 merged 2 commits into
masterfrom
feature/371-stuck-replica-resync-loop
Aug 13, 2026
Merged

feat(anomaly): detect replica full-resync failure loop (valkey#1836)#373
jamby77 merged 2 commits into
masterfrom
feature/371-stuck-replica-resync-loop

Conversation

@jamby77

@jamby77 jamby77 commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Extends the stuck-replica module with the full-resync failure-loop signature from valkey-io/valkey#1836: a replica that connects, starts a full sync, fails to load the transferred RDB (disk errors, corruption, permissions, out-of-space, a filesystem/AV layer mangling the transfer), disconnects and retries forever. master_link_status never leaves down, so the replica serves stale data indefinitely while looking "merely syncing" at any single glance.

Unlike the existing CLUSTER NODES snapshot detector in the module, this evaluator folds INFO replication/stats fields from the polled replica across polls, so it works in standalone and cluster mode alike.

Detection discipline (per the issue's guardrails)

  • Fires only after the link has been continuously down for 5 minutes AND ≥ 2 failed full-sync cycles were observed — a normal first sync of a large dataset is one attempt still progressing and stays silent.
  • Failed cycles are counted from sync_full deltas, falling back to master_sync_in_progress toggling off while the link stays down (for replicas whose own counter never moves).
  • A drop in master_link_down_since_seconds means the link DID reach up between polls (a sync completed) — the window restarts rather than firing.
  • Recovery (link up / promotion / non-replica) clears all state, so a later loop earns the window afresh and alerts again.
  • Same emit-retry contract as the module's other stateful detectors: the finding is re-produced until the emit succeeds, acknowledged only after.

Wiring

  • New RESYNC_LOOP metric type (state-based, added to METRICS_HANDLED_OUTSIDE_EXTRACTOR), WARNING severity, distinct message pointing at replica-side disk/permissions/RDB-integrity checks.
  • Called from the poll loop next to the COB-pressure detector (its replica-side complement); per-connection state cleaned up on connection removal.
  • Dashboard metric label: "Replica Resync Loop".

Test plan

  • 8 new unit tests covering the issue's acceptance criteria: healthy first sync of a large dataset (silent), genuine loop via sync_full (fires past window + cycles), loop via the in-progress toggle, silent before the window despite cycles, recovery clears + re-alerts on a fresh loop, emit-retry/acknowledge contract, non-replica reset, and the down-counter-drop window restart.
  • Full anomaly-detection suite: 506/506 green; tsc --noEmit clean for api and web.

Closes #371


Note

Medium Risk
New replication anomaly path on every poll for replica connections; logic is gated and well-tested, but false positives during flaky syncs could add noise until the window and cycle thresholds are met.

Overview
Adds state-based detection for replicas stuck in a full-resync failure loop (valkey#1836): master_link_status stays down while full-sync attempts start and fail repeatedly, so the replica can keep serving stale data.

The new evaluateResyncLoop logic in stuck-replica-detector tracks INFO replication across polls (standalone or cluster). It only fires after a 5-minute continuous link-down window and ≥2 failed cycles (sync in-progress ends while the link is still down). A long first sync that never drops in-progress stays silent; a drop in master_link_down_since_seconds resets the window. Recovery or promotion clears state so a later loop can alert again, with the same emit-retry / acknowledge pattern as other stateful detectors.

AnomalyService wires detectResyncLoop on each poll (beside COB pressure), registers MetricType.RESYNC_LOOP outside the z-score extractor, and cleans per-connection state on disconnect. The anomaly dashboard labels the metric Replica Resync Loop.

Eight unit tests cover healthy long sync, genuine loops, timing gates, recovery/re-alert, acknowledge dedupe, non-replica reset, and down-counter restart. Remaining diff in the dashboard file is mostly formatting.

Reviewed by Cursor Bugbot for commit 1ec3e30. Bugbot is set up for automated code reviews on this repo. Configure here.

- Extend stuck-replica module with a stateful INFO-based evaluator:
  link held down through 2+ full-sync attempts across a 5-minute window
  fires a WARNING; a single long first sync stays silent
- Count failed cycles from sync_full deltas, falling back to the
  master_sync_in_progress flag toggling off while the link stays down
- Reset on recovery, promotion, or a server-side down-counter drop (the
  link reached up between polls), so a later loop alerts afresh
- New resync_loop metric type wired into the poll loop + dashboard label

Closes #371
Comment thread proprietary/anomaly-detection/stuck-replica-detector.ts Outdated
Comment thread proprietary/anomaly-detection/stuck-replica-detector.ts
- Stop counting sync_full toward failed cycles: it tracks outbound full
  syncs this node served as a primary, so it never moves for the
  replica's own inbound loop, and downstream reconnects on an
  intermediate replica would fake failed cycles during a healthy first
  sync
- Failed cycles now come only from master_sync_in_progress falling back
  to 0 while the link stays down
- Accept role:replica alongside legacy role:slave, matching the rest of
  the service
@jamby77

jamby77 commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

@BugBot review

@jamby77

jamby77 commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

@claude review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 1ec3e30. Configure here.

@jamby77
jamby77 requested a review from KIvanow August 11, 2026 06:53

@KIvanow KIvanow left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Most/some of the nitpicks from #372 are applicable here as well, so I won't repeat them

@jamby77
jamby77 merged commit 8998788 into master Aug 13, 2026
3 checks passed
@jamby77
jamby77 deleted the feature/371-stuck-replica-resync-loop branch August 13, 2026 06:21
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 13, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extend stuck-replica to catch full-resync failure loop

2 participants