Fix false-positive slow alerts: require 3 consecutive checks + use P95 baseline
Previously a single check above 2× average response time fired an alert. This caused false positives from normal network variance. - scheduler.js: track consecutive slow checks per resource; only alert after 3 in a row (resets to 0 when response time normalises) - monitorService.js: calculateResponseTimeBaseline now returns P95 instead of mean, making 2× baseline a much more realistic slow threshold Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
98e0949d08
commit
dd85a79d55
2 files changed
+28
-19
No files matched your search
@@ -765,9 +765,10 @@ class MonitorService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return average of response times
|
// Use P95 so the baseline reflects realistic worst-case, not the average.
|
||||||
const sum = checks.reduce((acc, c) => acc + (c.response_time || 0), 0);
|
// Threshold is 2× P95, making it very unlikely to trigger on normal variance.
|
||||||
return Math.round(sum / checks.length);
|
const p95Index = Math.min(Math.floor(checks.length * 0.95), checks.length - 1);
|
||||||
|
return Math.round(checks[p95Index].response_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if response time is anomalous
|
// Check if response time is anomalous
|
||||||
|
|||||||
+24
-16
@@ -9,6 +9,7 @@ class Scheduler {
|
|||||||
this.isRunning = false;
|
this.isRunning = false;
|
||||||
this.lastCheckedAt = new Map();
|
this.lastCheckedAt = new Map();
|
||||||
this.slowAlertCooldown = new Map();
|
this.slowAlertCooldown = new Map();
|
||||||
|
this.slowConsecutiveCount = new Map(); // tracks consecutive slow checks per resource
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
@@ -172,26 +173,33 @@ class Scheduler {
|
|||||||
pendingAlerts.push({ resource, incident, stats });
|
pendingAlerts.push({ resource, incident, stats });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slow alert: prefer explicit threshold, fall back to 2× computed baseline
|
// Slow alert: prefer explicit threshold, fall back to 2× P95 baseline
|
||||||
const effectiveThreshold = resource.response_time_threshold ||
|
const effectiveThreshold = resource.response_time_threshold ||
|
||||||
(resource.response_time_baseline ? resource.response_time_baseline * 2 : null);
|
(resource.response_time_baseline ? resource.response_time_baseline * 2 : null);
|
||||||
|
|
||||||
if (
|
if (result.status === 'up' && effectiveThreshold && !resource.maintenance_mode) {
|
||||||
result.status === 'up' &&
|
if (result.response_time > effectiveThreshold) {
|
||||||
effectiveThreshold &&
|
const consecutive = (this.slowConsecutiveCount.get(resource.id) || 0) + 1;
|
||||||
result.response_time > effectiveThreshold &&
|
this.slowConsecutiveCount.set(resource.id, consecutive);
|
||||||
!resource.maintenance_mode
|
// Require 3 consecutive slow checks before alerting (avoids single-spike false positives)
|
||||||
) {
|
if (consecutive >= 3) {
|
||||||
const lastSlowAlert = this.slowAlertCooldown.get(resource.id) || 0;
|
const lastSlowAlert = this.slowAlertCooldown.get(resource.id) || 0;
|
||||||
if (now - lastSlowAlert > 60 * 60 * 1000) {
|
if (now - lastSlowAlert > 60 * 60 * 1000) {
|
||||||
this.slowAlertCooldown.set(resource.id, now);
|
this.slowAlertCooldown.set(resource.id, now);
|
||||||
await notificationService.sendAlert(resource, {
|
await notificationService.sendAlert(resource, {
|
||||||
type: 'slow',
|
type: 'slow',
|
||||||
responseTime: result.response_time,
|
responseTime: result.response_time,
|
||||||
threshold: effectiveThreshold,
|
threshold: effectiveThreshold,
|
||||||
id: null,
|
id: null,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Reset consecutive counter when response time is back to normal
|
||||||
|
this.slowConsecutiveCount.set(resource.id, 0);
|
||||||
}
|
}
|
||||||
|
} else if (!effectiveThreshold) {
|
||||||
|
this.slowConsecutiveCount.set(resource.id, 0);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Check error handled silently
|
// Check error handled silently
|
||||||
|
|||||||
Reference in new issue
Block a user