use async_trait::async_trait;
use thiserror::Error;
use crate::api::{
CostSeriesPoint, ErrorRateByModel, FinishReasonCount, LatencyStats, ModelUsage, RetryStats,
SystemUsage, TokenUsageSummary, ToolUsage, TopSpan,
};
use crate::query::QueryPredicate;
use crate::telemetry::log::SeverityLevel;
use crate::telemetry::{LogRecord, Metric, Span};
pub type Result<T> = std::result::Result<T, StorageError>;
#[derive(Error, Debug)]
pub enum StorageError {
#[error("Failed to initialize storage: {0}")]
InitializationError(String),
#[error("Failed to write data: {0}")]
WriteError(String),
#[error("Failed to query data: {0}")]
QueryError(String),
#[error("Insufficient disk space: {0}")]
DiskFullError(String),
#[error("Storage corruption detected: {0}")]
CorruptionError(String),
#[error("Permission denied: {0}")]
PermissionError(String),
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("Purge operation failed: {0}")]
PurgeError(String),
#[error("Database error: {0}")]
DatabaseError(String),
#[error("I/O error: {0}")]
IoError(String),
#[error("Serialization error: {0}")]
SerializationError(String),
}
impl StorageError {
pub fn is_recoverable(&self) -> bool {
matches!(
self,
StorageError::WriteError(_) | StorageError::QueryError(_) | StorageError::PurgeError(_)
)
}
pub fn is_corruption(&self) -> bool {
matches!(self, StorageError::CorruptionError(_))
}
pub fn is_disk_full(&self) -> bool {
matches!(self, StorageError::DiskFullError(_))
}
}
#[derive(Debug, Clone)]
pub struct PurgeAllStats {
pub logs_deleted: u64,
pub spans_deleted: u64,
pub metrics_deleted: u64,
}
#[derive(Debug, Clone)]
pub struct StorageStats {
pub log_count: u64,
pub span_count: u64,
pub metric_count: u64,
pub oldest_timestamp: Option<i64>,
pub newest_timestamp: Option<i64>,
pub storage_size_bytes: u64,
}
#[derive(Debug, Clone, Default)]
pub struct QueryParams {
pub start_time: Option<i64>,
pub end_time: Option<i64>,
pub limit: Option<usize>,
pub trace_id: Option<String>,
pub span_id: Option<String>,
pub min_severity: Option<SeverityLevel>,
pub search_text: Option<String>,
pub predicates: Vec<QueryPredicate>,
}
#[derive(Debug, Clone)]
pub struct PurgeOptions {
pub older_than: Option<i64>,
pub signal_types: Vec<SignalType>,
pub dry_run: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SignalType {
Logs,
Traces,
Metrics,
}
#[async_trait]
pub trait StorageBackend: Send + Sync {
async fn initialize(&mut self) -> Result<()>;
async fn write_log(&self, log: &LogRecord) -> Result<()>;
async fn write_span(&self, span: &Span) -> Result<()>;
async fn write_metric(&self, metric: &Metric) -> Result<()>;
async fn query_logs(&self, params: &QueryParams) -> Result<Vec<LogRecord>>;
async fn query_spans(&self, params: &QueryParams) -> Result<Vec<Span>>;
async fn query_spans_for_trace_list(
&self,
params: &QueryParams,
trace_limit: usize,
) -> Result<Vec<Span>>;
async fn query_metrics(&self, params: &QueryParams) -> Result<Vec<Metric>>;
async fn query_latest_metrics(&self, params: &QueryParams) -> Result<Vec<Metric>>;
async fn stats(&self) -> Result<StorageStats>;
async fn purge(&self, options: &PurgeOptions) -> Result<u64>;
async fn purge_all(&self) -> Result<PurgeAllStats>;
async fn close(&mut self) -> Result<()>;
async fn distinct_resource_keys(&self, signal: &str) -> Result<Vec<String>>;
async fn query_token_usage(
&self,
start_time: Option<i64>,
end_time: Option<i64>,
) -> Result<(TokenUsageSummary, Vec<ModelUsage>, Vec<SystemUsage>)>;
async fn query_cost_series(
&self,
start_time: Option<i64>,
end_time: Option<i64>,
bucket_ns: i64,
) -> Result<Vec<CostSeriesPoint>>;
async fn query_top_spans(
&self,
start_time: Option<i64>,
end_time: Option<i64>,
limit: usize,
) -> Result<Vec<TopSpan>>;
async fn query_finish_reasons(
&self,
start_time: Option<i64>,
end_time: Option<i64>,
) -> Result<Vec<FinishReasonCount>>;
async fn query_latency_stats(
&self,
start_time: Option<i64>,
end_time: Option<i64>,
) -> Result<Vec<LatencyStats>>;
async fn query_error_rate(
&self,
start_time: Option<i64>,
end_time: Option<i64>,
) -> Result<Vec<ErrorRateByModel>>;
async fn query_tool_usage(
&self,
start_time: Option<i64>,
end_time: Option<i64>,
limit: usize,
) -> Result<Vec<ToolUsage>>;
async fn query_retry_stats(
&self,
start_time: Option<i64>,
end_time: Option<i64>,
) -> Result<RetryStats>;
}