Skip to content

feat(anomaly): control-plane saturation correlated detector (valkey#3927) - #348

Merged
jamby77 merged 2 commits into
masterfrom
feature/control-plane-saturation-detector
Jul 29, 2026
Merged

feat(anomaly): control-plane saturation correlated detector (valkey#3927)#348
jamby77 merged 2 commits into
masterfrom
feature/control-plane-saturation-detector

Conversation

@jamby77

@jamby77 jamby77 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Detector #5 of the board batch — the last one, deliberately sequenced after the COB detector whose REPL_BUFFER_PRESSURE signal it corroborates against. Stacked on #347 (merge that first).

Surfaces the valkey#3927 condition: under CPU saturation the single-threaded event loop starves control-plane traffic — delayed heartbeats and replication events (false node timeouts, replica drops, unnecessary failovers) and admin commands queuing behind the data backlog. Until upstream's trusted-sources QoS lands, this pairs the observable signals.

Detection

The pairing is the whole design — CPU alone never fires, and an RTT blip alone never fires:

sustained CPU ≥90% for 3 consecutive polls
AND at least one of {
  probe-RTT spike (≥4x rolling median, 100ms floor),
  connected_slaves drop since previous poll,
  recent (≤60s) REPLICATION_ROLE / CLUSTER_STATE / CLUSTER_TOPOLOGY /
    RAFT_HEALTH / REPL_BUFFER_PRESSURE anomaly
} → one CRITICAL CONTROL_PLANE_SATURATION correlated group per episode

Episode cooldown: after firing, quiet until the CPU streak fully resets (recovery + re-trigger). The emit uses the ack-after-success contract so a failed emit retries next poll.

Mechanics

  • No new MetricType (PRD non-goal): the trigger is one synthetic CRITICAL CPU_UTILIZATION event carrying a structural marker (zScore 0, threshold 90) that isControlPlaneSaturationEvent recognizes — no message sniffing. Z-score CPU anomalies (always non-zero zScore) can't collide.
  • Correlator rule placed first so a mixed window containing the marker plus its own side effects (failover, cluster-state events) resolves to CONTROL_PLANE_SATURATION rather than NODE_FAILOVER.
  • Probe RTT measured around the poll's existing INFO call (performance.now delta) — zero added commands; server-side used_cpu_* remains the primary signal, RTT is corroboration only.
  • FAILOVER_CHURN is included as a corroborator: the enum member is mirrored verbatim from the feat(anomaly): gossip-mode failover-churn detector (valkey#3996) #346 branch, so whichever lands second rebases with a trivial dedupe. A churning shard during sustained saturation is direct starvation evidence.

Tests

13 pure-module cases (streak, RTT baseline/floor/spike, corroboration matrix, ack/cooldown/re-arm, null-sample carry) + 3 correlator cases (marker match, z-score non-match, mixed-window priority) + 5 service-integration cases (replica-drop fire, event corroboration, CPU-only silence, one-per-episode + re-arm, cleanup). Full anomaly-detection suite: 263 green. tsc clean.


Note

Medium Risk
New detection logic on the hot poll path can mis-alert on restarts or maintenance if corroboration rules misfire; correlator priority changes how mixed CPU/failover windows are labeled.

Overview
Adds control-plane saturation detection for valkey#3927: sustained ≥90% CPU for three polls only raises a CRITICAL when paired with impact evidence (probe INFO RTT spike, replica drops, or recent replication/cluster/COB-style anomalies). High CPU or RTT alone never fires.

A new control-plane-saturation-detector module owns episode state (streaks, RTT baseline, evidence floors, restart handling). AnomalyService measures probe RTT around getInfoParsed, tracks connected_slaves drops, runs detection after other poll detectors, and emits one synthetic CPU_UTILIZATION event per episode via syntheticPattern: CONTROL_PLANE_SATURATION (no new metric type). CPU z-score config stays threshold-free to avoid double alerts.

The correlator adds a first-priority CONTROL_PLANE_SATURATION rule so mixed windows resolve to saturation over NODE_FAILOVER. AnomalyEvent gains optional syntheticPattern; connection teardown clears saturation maps. Broad unit and service integration tests cover corroboration, cooldown, restart, and cleanup.

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

@jamby77 jamby77 changed the title feature/control plane saturation detector feat(anomaly): control-plane saturation correlated detector (valkey#3927) Jul 28, 2026
Comment thread proprietary/anomaly-detection/anomaly.service.ts
@jamby77
jamby77 force-pushed the feature/control-plane-saturation-detector branch from 2b83eb1 to 65c4bf9 Compare July 28, 2026 07:52
@jamby77

jamby77 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

bugbot run

Comment thread proprietary/anomaly-detection/anomaly.service.ts
@jamby77
jamby77 force-pushed the feature/control-plane-saturation-detector branch from 65c4bf9 to 0582eb8 Compare July 28, 2026 08:07
@jamby77

jamby77 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

bugbot run

@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 0582eb8. Configure here.

@jamby77
jamby77 force-pushed the feature/control-plane-saturation-detector branch from 0582eb8 to fe43931 Compare July 28, 2026 08:10
Comment thread proprietary/anomaly-detection/control-plane-saturation-detector.ts
@jamby77
jamby77 force-pushed the feature/control-plane-saturation-detector branch from fe43931 to 84a9fa0 Compare July 28, 2026 08:14
@jamby77

jamby77 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

bugbot run

1 similar comment
@jamby77

jamby77 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

bugbot run

@jamby77
jamby77 force-pushed the feature/control-plane-saturation-detector branch 2 times, most recently from dc85e3d to 47c5b88 Compare July 28, 2026 08:29
@jamby77

jamby77 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

bugbot run

@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 47c5b88. Configure here.

Comment thread proprietary/anomaly-detection/control-plane-saturation-detector.ts

@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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d476252. Configure here.

Comment thread proprietary/anomaly-detection/control-plane-saturation-detector.ts
@jamby77
jamby77 requested a review from KIvanow July 28, 2026 09:59

@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.

The detector itself is essentially merge-quality - the CPU-streak + impact-evidence pairing is the right design, and the restart-safety (streak/drop/RTT/event resets) and evidenceFloorAt stale-evidence gating are handled carefully and well-tested. Holding it as draft is the right call given the dependencies:

Blocking (sequencing):

  1. Stacked on unmerged #347 - can't land until that does.
  2. FAILOVER_CHURN enum duplicated with #346. It's added verbatim here, but nothing in this branch actually emits it, so its entry in CONTROL_PLANE_CORROBORATING_METRICS is a dead corroborator until #346 lands. Let #346 own the enum and drop it from this PR to avoid the duplicate-member conflict.

Design concern worth addressing before promoting out of draft:
3. The synthetic-event marker is implicit and fragile. isControlPlaneSaturationEvent recognizes the synthetic event by sniffing CPU_UTILIZATION AND CRITICAL AND zScore===0 AND threshold===90. This is collision-free today only because the CPU config has no absolute threshold - if anyone later adds criticalThreshold: 90 to the CPU config, a poll where value == mean == 90 produces exactly zScore=0, threshold=90 and gets misclassified. Recommend a dedicated marker field (explicit synthetic/anomalyType flag) and/or a single named constructor for the synthetic event, plus a warning comment at the CPU config guarding against adding an absolute threshold.
4. False-positive scenario is untested: a replica removed for maintenance while a primary sits busy-but-healthy at >=90% produces a single connected_slaves drop within the window -> CRITICAL. Rolling replica restarts under load are realistic - worth an explicit test (and maybe a second-signal requirement).

Verdict: draft - not ready. Sequence: land #347 -> land #346 (with the enum) -> rebase this, de-dupe the enum, add the false-positive test.

@jamby77

jamby77 commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed in 69c8b14 (stays draft until #347#346 land, per your sequencing):

  1. Sequencing — rebased onto feat(anomaly): replication output-buffer pressure detector (valkey#3963) #347's current head (2187a69), which includes its review fixes.
  2. Enum de-dupFAILOVER_CHURN dropped from this branch entirely (enum member, baseline-exclusion guard, and the dead CONTROL_PLANE_CORROBORATING_METRICS entry + its test). feat(anomaly): gossip-mode failover-churn detector (valkey#3996) #346 owns it; a comment marks where the corroborator gets re-added after the post-feat(anomaly): gossip-mode failover-churn detector (valkey#3996) #346 rebase.
  3. Synthetic marker — the numeric sniff is gone. AnomalyEvent gains an explicit optional syntheticPattern field (in-memory only, never persisted or inferred), stamped exclusively by a new createControlPlaneSaturationEvent constructor; isControlPlaneSaturationEvent now checks only that marker. Added the exact collision case you described as a regression test (unmarked CPU event with value=90/zScore=0/threshold=90 → not recognized), plus a guard comment at the CPU spike config warning against adding absolute thresholds (they'd double-alert alongside the synthetic event regardless of the marker).
  4. Maintenance false positive — implemented the second-signal requirement, graded: a lone single-replica drop (indistinguishable from maintenance) corroborates only alongside an RTT spike or a fresh control-plane anomaly; ≥2 drops in the window — one mass drop or repeated drops, the actual saturation signature — still stand alone. Your scenario (replica removed while the primary sits busy-but-healthy at ≥90%) is now an explicit no-fire test at both the detector and service level.

@jamby77
jamby77 force-pushed the feature/control-plane-saturation-detector branch from 69c8b14 to 3a97517 Compare July 29, 2026 16:57
Base automatically changed from feature/cob-pressure-detector to master July 29, 2026 17:02
@jamby77
jamby77 force-pushed the feature/control-plane-saturation-detector branch from 3a97517 to 6358f32 Compare July 29, 2026 17:02
@jamby77
jamby77 marked this pull request as ready for review July 29, 2026 17:03
jamby77 added 2 commits July 29, 2026 20:09
…927)

- Sustained ≥90% CPU streak paired with control-plane impact evidence:
  probe-RTT spike vs rolling baseline, graded replica drops (a lone
  single drop needs a second signal), or recent control-plane anomalies
- One synthetic CRITICAL CPU event per episode via a single constructor
  stamping an explicit syntheticPattern marker; correlator maps it to
  CONTROL_PLANE_SATURATION
- Restart-safety: CPU counter reset clears streak, drops, RTT baseline,
  and advances the evidence floor
- Re-add FAILOVER_CHURN to CONTROL_PLANE_CORROBORATING_METRICS now that
  #346 (the enum owner) is merged into the base
@jamby77
jamby77 force-pushed the feature/control-plane-saturation-detector branch from 6358f32 to 3aa05ea Compare July 29, 2026 17:09
@jamby77
jamby77 merged commit d94bec3 into master Jul 29, 2026
3 checks passed
@jamby77
jamby77 deleted the feature/control-plane-saturation-detector branch July 29, 2026 17:11
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 29, 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.

2 participants