1use async_trait::async_trait;
8use thiserror::Error;
9
10use crate::api::{
11 CacheHitRateByModel, CallsSeriesPoint, ConversationCostRow, ConversationDepthStats,
12 CostSeriesPoint, ErrorRateByModel, ErrorTypeBreakdown, FinishReasonCount, LatencyStats,
13 ModelDriftPair, ModelUsage, RequestParamProfile, RetrievalStats, RetryStats, SessionCostRow,
14 SystemUsage, TokenUsageSummary, ToolUsage, TopSpan, TopSpanSort, TruncationRateByModel,
15};
16use crate::query::QueryPredicate;
17use crate::telemetry::log::SeverityLevel;
18use crate::telemetry::{LogRecord, Metric, Span};
19
20pub type Result<T> = std::result::Result<T, StorageError>;
22
23#[derive(Error, Debug)]
29pub enum StorageError {
30 #[error("Failed to initialize storage: {0}")]
32 InitializationError(String),
33
34 #[error("Failed to write data: {0}")]
36 WriteError(String),
37
38 #[error("Failed to query data: {0}")]
40 QueryError(String),
41
42 #[error("Insufficient disk space: {0}")]
44 DiskFullError(String),
45
46 #[error("Storage corruption detected: {0}")]
48 CorruptionError(String),
49
50 #[error("Permission denied: {0}")]
52 PermissionError(String),
53
54 #[error("Configuration error: {0}")]
56 ConfigError(String),
57
58 #[error("Purge operation failed: {0}")]
60 PurgeError(String),
61
62 #[error("Database error: {0}")]
64 DatabaseError(String),
65
66 #[error("I/O error: {0}")]
68 IoError(String),
69
70 #[error("Serialization error: {0}")]
72 SerializationError(String),
73}
74
75impl StorageError {
76 pub fn is_recoverable(&self) -> bool {
77 matches!(
78 self,
79 StorageError::WriteError(_) | StorageError::QueryError(_) | StorageError::PurgeError(_)
80 )
81 }
82
83 pub fn is_corruption(&self) -> bool {
84 matches!(self, StorageError::CorruptionError(_))
85 }
86
87 pub fn is_disk_full(&self) -> bool {
88 matches!(self, StorageError::DiskFullError(_))
89 }
90}
91
92#[derive(Debug, Clone)]
94pub struct PurgeAllStats {
95 pub logs_deleted: u64,
96 pub spans_deleted: u64,
97 pub metrics_deleted: u64,
98}
99
100#[derive(Debug, Clone)]
102pub struct StorageStats {
103 pub log_count: u64,
104 pub span_count: u64,
105 pub metric_count: u64,
106 pub oldest_timestamp: Option<i64>,
108 pub newest_timestamp: Option<i64>,
110 pub storage_size_bytes: u64,
111}
112
113#[derive(Debug, Clone, Default)]
115pub struct QueryParams {
116 pub start_time: Option<i64>,
117 pub end_time: Option<i64>,
118 pub limit: Option<usize>,
119 pub trace_id: Option<String>,
120 pub span_id: Option<String>,
121 pub min_severity: Option<SeverityLevel>,
122 pub search_text: Option<String>,
123 pub predicates: Vec<QueryPredicate>,
124}
125
126#[derive(Debug, Clone)]
128pub struct PurgeOptions {
129 pub older_than: Option<i64>,
131 pub signal_types: Vec<SignalType>,
132 pub dry_run: bool,
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
137pub enum SignalType {
138 Logs,
139 Traces,
140 Metrics,
141}
142
143#[async_trait]
148pub trait StorageBackend: Send + Sync {
149 async fn initialize(&mut self) -> Result<()>;
150 async fn write_log(&self, log: &LogRecord) -> Result<()>;
151 async fn write_span(&self, span: &Span) -> Result<()>;
152 async fn write_metric(&self, metric: &Metric) -> Result<()>;
153 async fn query_logs(&self, params: &QueryParams) -> Result<Vec<LogRecord>>;
154 async fn query_spans(&self, params: &QueryParams) -> Result<Vec<Span>>;
155 async fn query_spans_for_trace_list(
157 &self,
158 params: &QueryParams,
159 trace_limit: usize,
160 ) -> Result<Vec<Span>>;
161 async fn query_metrics(&self, params: &QueryParams) -> Result<Vec<Metric>>;
163 async fn query_latest_metrics(&self, params: &QueryParams) -> Result<Vec<Metric>>;
165 async fn stats(&self) -> Result<StorageStats>;
166 async fn purge(&self, options: &PurgeOptions) -> Result<u64>;
167 async fn purge_all(&self) -> Result<PurgeAllStats>;
168 async fn close(&mut self) -> Result<()>;
169 async fn distinct_resource_keys(&self, signal: &str) -> Result<Vec<String>>;
172 async fn query_token_usage(
173 &self,
174 start_time: Option<i64>,
175 end_time: Option<i64>,
176 model: Option<&str>,
177 ) -> Result<(TokenUsageSummary, Vec<ModelUsage>, Vec<SystemUsage>)>;
178
179 async fn query_cost_series(
183 &self,
184 start_time: Option<i64>,
185 end_time: Option<i64>,
186 bucket_ns: i64,
187 model: Option<&str>,
188 ) -> Result<Vec<CostSeriesPoint>>;
189
190 #[allow(clippy::too_many_arguments)]
195 async fn query_top_spans(
196 &self,
197 start_time: Option<i64>,
198 end_time: Option<i64>,
199 limit: usize,
200 sort_by: TopSpanSort,
201 truncated_only: bool,
202 ) -> Result<Vec<TopSpan>>;
203
204 async fn query_top_sessions(
206 &self,
207 start_time: Option<i64>,
208 end_time: Option<i64>,
209 limit: usize,
210 ) -> Result<Vec<SessionCostRow>>;
211
212 async fn query_top_conversations(
214 &self,
215 start_time: Option<i64>,
216 end_time: Option<i64>,
217 limit: usize,
218 ) -> Result<Vec<ConversationCostRow>>;
219
220 async fn query_finish_reasons(
222 &self,
223 start_time: Option<i64>,
224 end_time: Option<i64>,
225 model: Option<&str>,
226 ) -> Result<Vec<FinishReasonCount>>;
227
228 async fn query_latency_stats(
230 &self,
231 start_time: Option<i64>,
232 end_time: Option<i64>,
233 model: Option<&str>,
234 ) -> Result<Vec<LatencyStats>>;
235
236 async fn query_error_rate(
238 &self,
239 start_time: Option<i64>,
240 end_time: Option<i64>,
241 model: Option<&str>,
242 ) -> Result<Vec<ErrorRateByModel>>;
243
244 async fn query_tool_usage(
246 &self,
247 start_time: Option<i64>,
248 end_time: Option<i64>,
249 limit: usize,
250 ) -> Result<Vec<ToolUsage>>;
251
252 async fn query_retry_stats(
254 &self,
255 start_time: Option<i64>,
256 end_time: Option<i64>,
257 ) -> Result<RetryStats>;
258
259 async fn query_retrieval_stats(
261 &self,
262 start_time: Option<i64>,
263 end_time: Option<i64>,
264 top_queries_limit: usize,
265 ) -> Result<RetrievalStats>;
266
267 async fn query_truncation_rate(
269 &self,
270 start_time: Option<i64>,
271 end_time: Option<i64>,
272 model: Option<&str>,
273 ) -> Result<Vec<TruncationRateByModel>>;
274
275 async fn query_cache_hit_rate(
277 &self,
278 start_time: Option<i64>,
279 end_time: Option<i64>,
280 model: Option<&str>,
281 ) -> Result<Vec<CacheHitRateByModel>>;
282
283 async fn query_request_param_profile(
285 &self,
286 start_time: Option<i64>,
287 end_time: Option<i64>,
288 ) -> Result<RequestParamProfile>;
289
290 async fn query_conversation_depth(
292 &self,
293 start_time: Option<i64>,
294 end_time: Option<i64>,
295 ) -> Result<ConversationDepthStats>;
296
297 async fn query_calls_series(
299 &self,
300 start_time: Option<i64>,
301 end_time: Option<i64>,
302 bucket_secs: u64,
303 ) -> Result<Vec<CallsSeriesPoint>>;
304
305 async fn query_error_types(
307 &self,
308 start_time: Option<i64>,
309 end_time: Option<i64>,
310 model: Option<&str>,
311 ) -> Result<Vec<ErrorTypeBreakdown>>;
312
313 async fn query_model_drift(
315 &self,
316 start_time: Option<i64>,
317 end_time: Option<i64>,
318 ) -> Result<Vec<ModelDriftPair>>;
319}