feat(settings): configurable anomaly detector thresholds via PATCH /settings/anomaly/detectors#206
Conversation
fe3a92b to
93684a1
Compare
| ...DETECTOR_DEFAULTS[metric], | ||
| ...overrides[metric], | ||
| }; | ||
| } |
There was a problem hiding this comment.
Partial overrides can invert warning/critical Z-score ordering
Medium Severity
resolveDetectorConfig naively merges overrides onto defaults without validating cross-field invariants. Because the DTO validator (CriticalGreaterThanWarningValidator) only checks warningZScore < criticalZScore when both fields are in the same request payload, a partial PATCH like { connections: { warningZScore: 9.5 } } passes validation but produces a resolved config where warningZScore (9.5) exceeds the default criticalZScore (3.0). The spike detector then operates with an inverted threshold, likely suppressing warnings entirely or producing nonsensical severity classifications.
Reviewed by Cursor Bugbot for commit 93684a1. Configure here.
There was a problem hiding this comment.
Fixed in settings.service.ts — updateDetectorConfig now validates the fully resolved state (defaults + stored + new delta) before persisting.
93684a1 to
0f4986e
Compare
/settings API0f4986e to
0fa7891
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0fa7891. Configure here.
0fa7891 to
5a13892
Compare
Co-authored-by: Cursor <cursoragent@cursor.com>
5a13892 to
7e27cb0
Compare


Summary
Anomaly detector thresholds were hardcoded in
proprietary/anomaly-detection/anomaly.service.tsand required a code change to tune — as noted in the anomaly detection tuning guide ("requires code change, or wait for configurable detectors"). This PR wires all per-metric thresholds through the settings API so operators can adjust sensitivity at runtime with no restart.Changes
packages/sharedanomaly-detector-settings.types.ts— New.AnomalyDetectorConfigEntryandAnomalyDetectorConfigMaptypes shared across API and proprietary modules.anomaly.ts— AddsreloadDetectorConfig(overrides)toIAnomalyServiceinterface.settings.types.ts— AddsanomalyDetectorConfig: AnomalyDetectorConfigMaptoAppSettings.apps/api/src/anomaly/anomaly.types.ts— New file.MetricTypeenum (11 configurable metrics;replication_roleandcpu_utilizationexcluded — state-diff and proprietary-only respectively).DetectorConfiginterface,DETECTOR_DEFAULTSconst mirroring previous hardcoded values,resolveDetectorConfig()merge helper,toSpikeDetectorConfig()adapter,DEFAULT_SPIKE_CONFIGfor non-API metrics.apps/api/src/settings/dto/update-anomaly-detectors.dto.ts— New file.DetectorConfigDtowith range validation on all 6 fields plus a customCriticalGreaterThanWarningValidatorcross-field guard.UpdateAnomalyDetectorsDtowith one optionalDetectorConfigDtoperMetricType.apps/api/src/settings/settings.service.tsTwo new methods:
getDetectorConfig()reads stored overrides from cached settings.updateDetectorConfig()does field-level merge (not whole-metric replace), validates the fully resolved cross-field invariants (warningZScore < criticalZScore) against the merged + default state, then persists and returns the merged map.apps/api/src/settings/settings.controller.tsGET /settings/anomaly/detectors— returns{ defaults, overrides, resolved }.resolvedis every metric fully merged, safe to read directly.PATCH /settings/anomaly/detectors— validates, persists, hot-reloads viaanomalyService.reloadDetectorConfig().AnomalyServiceinjected as@Optional()for testability; startup warning logged if absent.POST /settings/anomaly/detectors/reset— clears all overrides and hot-reloads to defaults.Storage adapters (postgres, sqlite, memory, base-sql)
anomaly_detector_configcolumn added via additive migration (SQLite) andIF NOT EXISTS(Postgres). Memory adapter handles the new field. All upserts include the new column.proprietary/anomaly-detection/anomaly.service.tsonModuleInitmade async; loads stored overrides before polling begins.SpikeDetectorConfigobjects replaced withresolveSpikeConfig(metric)which routes throughresolveDetectorConfig()for API metrics andDEFAULT_SPIKE_CONFIGfor non-API metrics (cpu_utilization).reloadDetectorConfig(overrides)swaps the override map and callsapplyDetectorConfigToAllConnections()— iterates all live detectors and callsdetector.updateConfig(). Circular buffers are not reset.SLOWLOG_LAST_IDdetector creation updated to useresolveSpikeConfig.proprietary/anomaly-detection/spike-detector.tsupdateConfig(config)method added — replaces threshold fields in-place, preservesdetectDrops.apps/api/src/prometheus/prometheus.service.tsbetterdb_detector_config_updates_totalcounter incremented on each successful PATCH.Behavior
{ connections: { warningZScore: 2.5 } }only changes that one field —consecutiveRequired,cooldownMsetc. are untouched.Testing
settings-anomaly-detectors.spec.ts— 8 unit tests: default resolution, partial override, field-level merge, invalid range (400), unknown metric key (400), warningZScore ≥ criticalZScore in payload (400), partial PATCH inverting thresholdsagainst stored config (400), persist + hot-reload call chain.
proprietary/anomaly-detection/__tests__/anomaly.service.spec.ts— tests forreloadDetectorConfigandapplyDetectorConfigToAllConnections.anomalyDetectorConfig: {}where needed.API Reference
Note
Medium Risk
Introduces new persisted settings and hot-reload paths that directly affect anomaly detection behavior across running connections, plus DB schema migrations to add
anomaly_detector_config.Overview
Adds runtime-configurable per-metric anomaly detector thresholds via
GET/PATCH /settings/anomaly/detectorsandPOST /settings/anomaly/detectors/reset, including DTO validation, default/override resolution, field-level merge semantics, and cross-field safety checks before persisting.Persists overrides in
AppSettings(anomaly_detector_config) across Postgres/SQLite/Memory adapters, updates the proprietary anomaly service to load overrides on startup and hot-reload detector configs in-place (newSpikeDetector.updateConfig), and adds abetterdb_detector_config_updates_totalPrometheus counter. Also adds a SQLite guard to enforce uniqueness of pendingcache_proposalswhere partial indexes may be unreliable.Reviewed by Cursor Bugbot for commit 7e27cb0. Bugbot is set up for automated code reviews on this repo. Configure here.