@@ -20,28 +20,35 @@ use std::sync::Arc;
2020
2121use actix_web:: http:: header:: ContentType ;
2222use arrow_schema:: Schema ;
23- use chrono:: Utc ;
23+ use chrono:: { TimeDelta , Utc } ;
2424use http:: StatusCode ;
25+ use itertools:: Itertools ;
2526use serde:: { Deserialize , Serialize } ;
27+ use serde_json:: { Value , json} ;
2628use tracing:: warn;
2729
2830use crate :: {
2931 LOCK_EXPECT ,
32+ alerts:: alert_structs:: { ConditionConfig , Conditions } ,
33+ event:: DEFAULT_TIMESTAMP_KEY ,
3034 handlers:: http:: {
3135 cluster:: {
3236 fetch_stats_from_ingestors,
3337 utils:: { IngestionStats , QueriedStats , StorageStats , merge_queried_stats} ,
3438 } ,
3539 logstream:: error:: StreamError ,
36- query:: { QueryError , update_schema_when_distributed} ,
40+ query:: { Query , QueryError , get_records_and_fields , update_schema_when_distributed} ,
3741 } ,
3842 hottier:: { HotTierError , HotTierManager , StreamHotTier } ,
3943 parseable:: { PARSEABLE , StreamNotFound } ,
40- query:: { CountsRequest , CountsResponse , error:: ExecuteError } ,
44+ query:: { CountConditions , CountsRequest , CountsResponse , error:: ExecuteError } ,
4145 rbac:: { Users , map:: SessionKey , role:: Action } ,
4246 stats,
4347 storage:: { StreamInfo , StreamType , retention:: Retention } ,
44- utils:: time:: TimeParseError ,
48+ utils:: {
49+ arrow:: record_batches_to_json,
50+ time:: { TimeParseError , truncate_to_minute} ,
51+ } ,
4552 validator:: error:: HotTierValidationError ,
4653} ;
4754
@@ -218,7 +225,7 @@ pub struct PrismDatasetResponse {
218225
219226/// Request parameters for retrieving Prism dataset information.
220227/// Defines which streams to query
221- #[ derive( Deserialize , Default ) ]
228+ #[ derive( Deserialize , Default , Serialize ) ]
222229#[ serde( rename_all = "camelCase" ) ]
223230pub struct PrismDatasetRequest {
224231 /// List of stream names to query
@@ -292,7 +299,7 @@ impl PrismDatasetRequest {
292299
293300 // Process stream data
294301 match get_prism_logstream_info ( & stream) . await {
295- Ok ( info) => Ok ( Some ( self . build_dataset_response ( stream, info) . await ?) ) ,
302+ Ok ( info) => Ok ( Some ( self . build_dataset_response ( stream, info, & key ) . await ?) ) ,
296303 Err ( err) => Err ( err) ,
297304 }
298305 }
@@ -312,12 +319,13 @@ impl PrismDatasetRequest {
312319 & self ,
313320 stream : String ,
314321 info : PrismLogstreamInfo ,
322+ key : & SessionKey ,
315323 ) -> Result < PrismDatasetResponse , PrismLogstreamError > {
316324 // Get hot tier info
317325 let hottier = self . get_hot_tier_info ( & stream) . await ?;
318326
319327 // Get counts
320- let counts = self . get_counts ( & stream) . await ?;
328+ let counts = self . get_counts ( & stream, key ) . await ?;
321329
322330 Ok ( PrismDatasetResponse {
323331 stream,
@@ -346,20 +354,84 @@ impl PrismDatasetRequest {
346354 }
347355 }
348356
349- async fn get_counts ( & self , stream : & str ) -> Result < CountsResponse , PrismLogstreamError > {
357+ async fn get_counts (
358+ & self ,
359+ stream : & str ,
360+ key : & SessionKey ,
361+ ) -> Result < CountsResponse , PrismLogstreamError > {
362+ let end = truncate_to_minute ( Utc :: now ( ) ) ;
363+ let start = end - TimeDelta :: hours ( 1 ) ;
364+
365+ let conditions = if PARSEABLE . get_stream ( stream) ?. get_time_partition ( ) . is_some ( ) {
366+ Some ( CountConditions {
367+ conditions : Some ( Conditions {
368+ operator : Some ( crate :: alerts:: LogicalOperator :: And ) ,
369+ condition_config : vec ! [
370+ ConditionConfig {
371+ column: DEFAULT_TIMESTAMP_KEY . into( ) ,
372+ operator: crate :: alerts:: WhereConfigOperator :: GreaterThanOrEqual ,
373+ value: Some ( start. to_rfc3339( ) ) ,
374+ } ,
375+ ConditionConfig {
376+ column: DEFAULT_TIMESTAMP_KEY . into( ) ,
377+ operator: crate :: alerts:: WhereConfigOperator :: LessThan ,
378+ value: Some ( end. to_rfc3339( ) ) ,
379+ } ,
380+ ] ,
381+ } ) ,
382+ group_by : None ,
383+ } )
384+ } else {
385+ None
386+ } ;
387+
350388 let count_request = CountsRequest {
351389 stream : stream. to_owned ( ) ,
352- start_time : "1h" . to_owned ( ) ,
353- end_time : "now" . to_owned ( ) ,
390+ start_time : start . to_rfc3339 ( ) ,
391+ end_time : end . to_rfc3339 ( ) ,
354392 num_bins : 10 ,
355- conditions : None ,
393+ conditions,
356394 } ;
357395
358- let records = count_request. get_bin_density ( ) . await ?;
359- Ok ( CountsResponse {
360- fields : vec ! [ "start_time" . into( ) , "end_time" . into( ) , "count" . into( ) ] ,
361- records,
362- } )
396+ if count_request. conditions . is_some ( ) {
397+ // forward request to querier
398+ let query = count_request
399+ . get_df_sql ( DEFAULT_TIMESTAMP_KEY . into ( ) )
400+ . await ?;
401+
402+ let query_request = Query {
403+ query,
404+ start_time : start. to_rfc3339 ( ) ,
405+ end_time : end. to_rfc3339 ( ) ,
406+ send_null : true ,
407+ fields : true ,
408+ streaming : false ,
409+ filter_tags : None ,
410+ } ;
411+
412+ let ( records, _) = get_records_and_fields ( & query_request, key) . await ?;
413+ if let Some ( records) = records {
414+ let json_records = record_batches_to_json ( & records) ?;
415+ let records = json_records. into_iter ( ) . map ( Value :: Object ) . collect_vec ( ) ;
416+
417+ let res = json ! ( {
418+ "fields" : vec![ "start_time" , "end_time" , "count" ] ,
419+ "records" : records,
420+ } ) ;
421+
422+ Ok ( serde_json:: from_value ( res) ?)
423+ } else {
424+ Err ( PrismLogstreamError :: Anyhow ( anyhow:: Error :: msg (
425+ "No data returned for counts SQL" ,
426+ ) ) )
427+ }
428+ } else {
429+ let records = count_request. get_bin_density ( ) . await ?;
430+ Ok ( CountsResponse {
431+ fields : vec ! [ "start_time" . into( ) , "end_time" . into( ) , "count" . into( ) ] ,
432+ records,
433+ } )
434+ }
363435 }
364436}
365437
@@ -381,6 +453,10 @@ pub enum PrismLogstreamError {
381453 Execute ( #[ from] ExecuteError ) ,
382454 #[ error( "Auth: {0}" ) ]
383455 Auth ( #[ from] actix_web:: Error ) ,
456+ #[ error( "SerdeError: {0}" ) ]
457+ SerdeError ( #[ from] serde_json:: Error ) ,
458+ #[ error( "ReqwestError: {0}" ) ]
459+ ReqwestError ( #[ from] reqwest:: Error ) ,
384460}
385461
386462impl actix_web:: ResponseError for PrismLogstreamError {
@@ -393,6 +469,8 @@ impl actix_web::ResponseError for PrismLogstreamError {
393469 PrismLogstreamError :: Query ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
394470 PrismLogstreamError :: TimeParse ( _) => StatusCode :: NOT_FOUND ,
395471 PrismLogstreamError :: Execute ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
472+ PrismLogstreamError :: SerdeError ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
473+ PrismLogstreamError :: ReqwestError ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
396474 PrismLogstreamError :: Auth ( _) => StatusCode :: UNAUTHORIZED ,
397475 }
398476 }
0 commit comments