|
16 | 16 | * |
17 | 17 | */ |
18 | 18 |
|
| 19 | +use chrono::{DateTime, TimeZone, Utc}; |
| 20 | +use datafusion::arrow::json::writer::record_batches_to_json_rows; |
| 21 | +use datafusion::arrow::record_batch::RecordBatch; |
| 22 | +use datafusion::error::DataFusionError; |
| 23 | + |
19 | 24 | use crate::metrics::{EVENTS_INGESTED, EVENTS_INGESTED_SIZE, STORAGE_SIZE}; |
| 25 | +use crate::query; |
| 26 | +use crate::query::error::ExecuteError; |
20 | 27 |
|
21 | 28 | /// Helper struct type created by copying stats values from metadata |
22 | 29 | #[derive(Debug, Default, serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Eq)] |
@@ -71,3 +78,49 @@ fn event_labels<'a>(stream_name: &'a str, format: &'static str) -> [&'a str; 2] |
71 | 78 | fn storage_size_labels(stream_name: &str) -> [&str; 3] { |
72 | 79 | ["data", stream_name, "parquet"] |
73 | 80 | } |
| 81 | + |
| 82 | +pub async fn get_first_event_at(stream_name: &str) -> Result<Option<String>, QueryError> { |
| 83 | + let query_string = format!( |
| 84 | + "SELECT p_timestamp FROM {} ORDER BY p_timestamp LIMIT 1", |
| 85 | + stream_name |
| 86 | + ); |
| 87 | + let first_time: DateTime<Utc> = Utc |
| 88 | + .timestamp_opt(0, 0) |
| 89 | + .single() |
| 90 | + .expect("Failed to get the first UTC time"); |
| 91 | + let now_time: DateTime<Utc> = Utc::now(); |
| 92 | + let session_state = query::QUERY_SESSION.state(); |
| 93 | + let logical_plan = session_state.create_logical_plan(&query_string).await?; |
| 94 | + |
| 95 | + let query = query::Query { |
| 96 | + raw_logical_plan: logical_plan, |
| 97 | + start: first_time, |
| 98 | + end: now_time, |
| 99 | + filter_tag: Some(Vec::new()), |
| 100 | + }; |
| 101 | + |
| 102 | + let (records, _fields) = query.execute().await?; |
| 103 | + let records_itr: Vec<&RecordBatch> = records.iter().collect(); |
| 104 | + let json_records = record_batches_to_json_rows(&records_itr).unwrap(); |
| 105 | + |
| 106 | + if let Some(single_record) = json_records.first() { |
| 107 | + if let Some(p_timestamp_value) = single_record.get("p_timestamp") { |
| 108 | + let p_timestamp_str = p_timestamp_value.as_str().unwrap_or_default(); |
| 109 | + return Ok(Some(p_timestamp_str.to_string())); |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + Ok(None) |
| 114 | +} |
| 115 | + |
| 116 | +#[derive(Debug, thiserror::Error)] |
| 117 | +pub enum QueryError { |
| 118 | + #[error("While generating times for 'now' failed to parse duration")] |
| 119 | + NotValidDuration(#[from] humantime::DurationError), |
| 120 | + #[error("Parsed duration out of range")] |
| 121 | + OutOfRange(#[from] chrono::OutOfRangeError), |
| 122 | + #[error("Datafusion Error: {0}")] |
| 123 | + Datafusion(#[from] DataFusionError), |
| 124 | + #[error("Query execution failed due to {0}")] |
| 125 | + Execute(#[from] ExecuteError), |
| 126 | +} |
0 commit comments