Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions src/alerts/alert_structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -610,6 +610,32 @@ pub struct AggregatedMTTRStats {
pub per_alert_stats: HashMap<String, MTTRStats>,
}

/// 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<DailyMTTRStats>,
}

/// Query parameters for MTTR API endpoint
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MTTRQueryParams {
pub start_date: Option<String>,
pub end_date: Option<String>,
}

impl AggregatedMTTRStats {
/// Calculate aggregated MTTR stats from multiple alert state entries
pub fn from_alert_states(alert_states: Vec<AlertStateEntry>) -> Self {
Expand Down Expand Up @@ -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()
}
}
9 changes: 8 additions & 1 deletion src/metastore/metastore_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<Option<MTTRHistory>, MetastoreError>;
async fn put_mttr_history(&self, obj: &dyn MetastoreObject) -> Result<(), MetastoreError>;

/// llmconfig
async fn get_llmconfigs(&self) -> Result<Vec<Bytes>, MetastoreError>;
async fn put_llmconfig(&self, obj: &dyn MetastoreObject) -> Result<(), MetastoreError>;
Expand Down
32 changes: 27 additions & 5 deletions src/metastore/metastores/object_store_metastore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -306,6 +306,28 @@ impl Metastore for ObjectStoreMetastore {
.await?)
}

/// Get MTTR history from storage
async fn get_mttr_history(&self) -> Result<Option<MTTRHistory>, 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::<MTTRHistory>(&bytes) {
Ok(Some(history))
} else {
Ok(None)
}
}
Err(ObjectStorageError::NoSuchKey(_)) => Ok(None),
Err(e) => Err(MetastoreError::ObjectStorageError(e)),
}
}
Comment on lines +309 to +323
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Use the mttr_json_path() helper for consistency.

Line 311 hardcodes the path construction, while put_mttr_history (Line 327) uses obj.get_object_path() which calls the mttr_json_path() helper. This creates an inconsistency and violates DRY. Consider using the helper directly for better maintainability.

Apply this diff:

-    async fn get_mttr_history(&self) -> Result<Option<MTTRHistory>, MetastoreError> {
-        let path = RelativePathBuf::from_iter([ALERTS_ROOT_DIRECTORY, "mttr.json"]);
+    async fn get_mttr_history(&self) -> Result<Option<MTTRHistory>, MetastoreError> {
+        let path = mttr_json_path();
         match self.storage.get_object(&path).await {

Note: You'll need to import mttr_json_path from crate::storage::object_storage at the top of the file.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/metastore/metastores/object_store_metastore.rs around lines 309 to 323,
the get_mttr_history function hardcodes the path with
RelativePathBuf::from_iter([ALERTS_ROOT_DIRECTORY, "mttr.json"]) instead of
using the existing mttr_json_path helper; replace the hardcoded path with a call
to mttr_json_path() (or obj.get_object_path()/equivalent helper used elsewhere)
and add an import for mttr_json_path from crate::storage::object_storage at the
top of the file so the same path construction is reused consistently.


/// 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<Vec<Bytes>, MetastoreError> {
let base_path = RelativePathBuf::from_iter([SETTINGS_ROOT_DIRECTORY, "llmconfigs"]);
Expand Down
9 changes: 8 additions & 1 deletion src/storage/object_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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([
Expand All @@ -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()
Expand Down
Loading