Implement hysteresis for metric status changes to prevent flapping

Add comprehensive hysteresis support to prevent status oscillation near
threshold boundaries while maintaining responsive alerting.

Key Features:
- HysteresisThresholds with configurable upper/lower limits
- StatusTracker for per-metric status history
- Default gaps: CPU load 10%, memory 5%, disk temp 5°C

Updated Components:
- CPU load collector (5-minute average with hysteresis)
- Memory usage collector (percentage-based thresholds)
- Disk temperature collector (SMART data monitoring)
- All collectors updated to support StatusTracker interface

Cache Interval Adjustments:
- Service status: 60s → 10s (faster response)
- Disk usage: 300s → 60s (more frequent checks)
- Backup status: 900s → 60s (quicker updates)
- SMART data: moved to 600s tier (10 minutes)

Architecture:
- Individual metric status calculation in collectors
- Centralized StatusTracker in MetricCollectionManager
- Status aggregation preserved in dashboard widgets
This commit is contained in:
2025-10-20 18:45:41 +02:00
parent e998679901
commit 00a8ed3da2
34 changed files with 1037 additions and 770 deletions

View File

@@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use crate::metrics::Metric;
use serde::{Deserialize, Serialize};
/// Message sent from agent to dashboard via ZMQ
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -65,28 +65,28 @@ impl MessageEnvelope {
payload: serde_json::to_vec(&message)?,
})
}
pub fn command(command: Command) -> Result<Self, crate::SharedError> {
Ok(Self {
message_type: MessageType::Command,
payload: serde_json::to_vec(&command)?,
})
}
pub fn command_response(response: CommandResponse) -> Result<Self, crate::SharedError> {
Ok(Self {
message_type: MessageType::CommandResponse,
payload: serde_json::to_vec(&response)?,
})
}
pub fn heartbeat() -> Result<Self, crate::SharedError> {
Ok(Self {
message_type: MessageType::Heartbeat,
payload: Vec::new(),
})
}
pub fn decode_metrics(&self) -> Result<MetricMessage, crate::SharedError> {
match self.message_type {
MessageType::Metrics => Ok(serde_json::from_slice(&self.payload)?),
@@ -95,7 +95,7 @@ impl MessageEnvelope {
}),
}
}
pub fn decode_command(&self) -> Result<Command, crate::SharedError> {
match self.message_type {
MessageType::Command => Ok(serde_json::from_slice(&self.payload)?),
@@ -104,7 +104,7 @@ impl MessageEnvelope {
}),
}
}
pub fn decode_command_response(&self) -> Result<CommandResponse, crate::SharedError> {
match self.message_type {
MessageType::CommandResponse => Ok(serde_json::from_slice(&self.payload)?),
@@ -113,4 +113,4 @@ impl MessageEnvelope {
}),
}
}
}
}