diff --git a/src/alerts/alert_structs.rs b/src/alerts/alert_structs.rs index 3c9f0b218..649e1d869 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 tokio::sync::{RwLock, mpsc}; use ulid::Ulid; @@ -35,7 +35,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}, }; /// Helper struct for basic alert fields during migration @@ -610,6 +610,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 { @@ -775,3 +801,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 b93984f05..4ace2e342 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}, @@ -110,10 +113,7 @@ impl Metastore for ObjectStoreMetastore { /// Delete an overview async fn delete_overview(&self, stream: &str) -> Result<(), MetastoreError> { let path = RelativePathBuf::from_iter([stream, "overview"]); - Ok(self - .storage - .delete_object(&path) - .await?) + Ok(self.storage.delete_object(&path).await?) } /// This function fetches all the keystones from the underlying object store @@ -306,6 +306,28 @@ impl Metastore for ObjectStoreMetastore { .await?) } + /// Get MTTR history from storage + async fn get_mttr_history(&self) -> Result, MetastoreError> { + let path = RelativePathBuf::from_iter([ALERTS_ROOT_DIRECTORY, "mttr.json"]); + 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()