diff --git a/src/alerts/alert_structs.rs b/src/alerts/alert_structs.rs index f484ac9c3..a37927626 100644 --- a/src/alerts/alert_structs.rs +++ b/src/alerts/alert_structs.rs @@ -18,7 +18,7 @@ use std::{collections::HashMap, time::Duration}; -use chrono::{DateTime, Utc}; +use chrono::{DateTime, NaiveDate, Utc}; use serde::{Deserialize, Serialize}; use serde_json::Value; use tokio::sync::{RwLock, mpsc}; @@ -36,7 +36,7 @@ use crate::{ }, metastore::metastore_traits::MetastoreObject, query::resolve_stream_names, - storage::object_storage::{alert_json_path, alert_state_json_path}, + storage::object_storage::{alert_json_path, alert_state_json_path, mttr_json_path}, }; const RESERVED_FIELDS: &[&str] = &[ @@ -695,6 +695,32 @@ pub struct AggregatedMTTRStats { pub per_alert_stats: HashMap, } +/// Daily MTTR statistics for a specific date +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DailyMTTRStats { + /// Date in YYYY-MM-DD format + pub date: NaiveDate, + /// Aggregated MTTR statistics for this date + pub stats: AggregatedMTTRStats, +} + +/// MTTR history containing array of daily MTTR objects +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MTTRHistory { + /// Array of daily MTTR statistics + pub daily_stats: Vec, +} + +/// Query parameters for MTTR API endpoint +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MTTRQueryParams { + pub start_date: Option, + pub end_date: Option, +} + impl AggregatedMTTRStats { /// Calculate aggregated MTTR stats from multiple alert state entries pub fn from_alert_states(alert_states: Vec) -> Self { @@ -860,3 +886,13 @@ impl MetastoreObject for AlertConfig { alert_json_path(self.id).to_string() } } + +impl MetastoreObject for MTTRHistory { + fn get_object_id(&self) -> String { + "mttr".to_string() + } + + fn get_object_path(&self) -> String { + mttr_json_path().to_string() + } +} diff --git a/src/metastore/metastore_traits.rs b/src/metastore/metastore_traits.rs index 27e56e547..ed4ea6f20 100644 --- a/src/metastore/metastore_traits.rs +++ b/src/metastore/metastore_traits.rs @@ -27,7 +27,10 @@ use tonic::async_trait; use ulid::Ulid; use crate::{ - alerts::{alert_structs::AlertStateEntry, target::Target}, + alerts::{ + alert_structs::{AlertStateEntry, MTTRHistory}, + target::Target, + }, catalog::manifest::Manifest, handlers::http::modal::NodeType, metastore::MetastoreError, @@ -77,6 +80,10 @@ pub trait Metastore: std::fmt::Debug + Send + Sync { async fn put_alert_state(&self, obj: &dyn MetastoreObject) -> Result<(), MetastoreError>; async fn delete_alert_state(&self, obj: &dyn MetastoreObject) -> Result<(), MetastoreError>; + /// mttr history + async fn get_mttr_history(&self) -> Result, MetastoreError>; + async fn put_mttr_history(&self, obj: &dyn MetastoreObject) -> Result<(), MetastoreError>; + /// llmconfig async fn get_llmconfigs(&self) -> Result, MetastoreError>; async fn put_llmconfig(&self, obj: &dyn MetastoreObject) -> Result<(), MetastoreError>; diff --git a/src/metastore/metastores/object_store_metastore.rs b/src/metastore/metastores/object_store_metastore.rs index 446e7daf8..d509bdfcc 100644 --- a/src/metastore/metastores/object_store_metastore.rs +++ b/src/metastore/metastores/object_store_metastore.rs @@ -32,7 +32,10 @@ use tracing::warn; use ulid::Ulid; use crate::{ - alerts::{alert_structs::AlertStateEntry, target::Target}, + alerts::{ + alert_structs::{AlertStateEntry, MTTRHistory}, + target::Target, + }, catalog::{manifest::Manifest, partition_path}, handlers::http::{ modal::{Metadata, NodeMetadata, NodeType}, @@ -49,7 +52,7 @@ use crate::{ SETTINGS_ROOT_DIRECTORY, STREAM_METADATA_FILE_NAME, STREAM_ROOT_DIRECTORY, TARGETS_ROOT_DIRECTORY, object_storage::{ - alert_json_path, alert_state_json_path, filter_path, manifest_path, + alert_json_path, alert_state_json_path, filter_path, manifest_path, mttr_json_path, parseable_json_path, schema_path, stream_json_path, to_bytes, }, }, @@ -305,6 +308,28 @@ impl Metastore for ObjectStoreMetastore { .await?) } + /// Get MTTR history from storage + async fn get_mttr_history(&self) -> Result, MetastoreError> { + let path = mttr_json_path(); + match self.storage.get_object(&path).await { + Ok(bytes) => { + if let Ok(history) = serde_json::from_slice::(&bytes) { + Ok(Some(history)) + } else { + Ok(None) + } + } + Err(ObjectStorageError::NoSuchKey(_)) => Ok(None), + Err(e) => Err(MetastoreError::ObjectStorageError(e)), + } + } + + /// Put MTTR history to storage + async fn put_mttr_history(&self, obj: &dyn MetastoreObject) -> Result<(), MetastoreError> { + let path = RelativePathBuf::from(obj.get_object_path()); + Ok(self.storage.put_object(&path, to_bytes(obj)).await?) + } + /// This function fetches all the llmconfigs from the underlying object store async fn get_llmconfigs(&self) -> Result, MetastoreError> { let base_path = RelativePathBuf::from_iter([SETTINGS_ROOT_DIRECTORY, "llmconfigs"]); diff --git a/src/storage/object_storage.rs b/src/storage/object_storage.rs index d3f38e4ba..907a7ce46 100644 --- a/src/storage/object_storage.rs +++ b/src/storage/object_storage.rs @@ -1152,7 +1152,7 @@ pub fn target_json_path(target_id: &Ulid) -> RelativePathBuf { } /// Constructs the path for storing alert state JSON files -/// Format: ".parseable/alerts/alert_state_{alert_id}.json" +/// Format: ".alerts/alert_state_{alert_id}.json" #[inline(always)] pub fn alert_state_json_path(alert_id: Ulid) -> RelativePathBuf { RelativePathBuf::from_iter([ @@ -1161,6 +1161,13 @@ pub fn alert_state_json_path(alert_id: Ulid) -> RelativePathBuf { ]) } +/// Constructs the path for storing MTTR history JSON file +/// Format: ".alerts/mttr.json" +#[inline(always)] +pub fn mttr_json_path() -> RelativePathBuf { + RelativePathBuf::from_iter([ALERTS_ROOT_DIRECTORY, "mttr.json"]) +} + #[inline(always)] pub fn manifest_path(prefix: &str) -> RelativePathBuf { let hostname = hostname::get()