@@ -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
@@ -216,7 +223,7 @@ pub struct PrismDatasetResponse {
216223
217224/// Request parameters for retrieving Prism dataset information.
218225/// Defines which streams to query
219- #[ derive( Deserialize , Default ) ]
226+ #[ derive( Deserialize , Default , Serialize ) ]
220227#[ serde( rename_all = "camelCase" ) ]
221228pub struct PrismDatasetRequest {
222229 /// List of stream names to query
@@ -290,7 +297,7 @@ impl PrismDatasetRequest {
290297
291298 // Process stream data
292299 match get_prism_logstream_info ( & stream) . await {
293- Ok ( info) => Ok ( Some ( self . build_dataset_response ( stream, info) . await ?) ) ,
300+ Ok ( info) => Ok ( Some ( self . build_dataset_response ( stream, info, & key ) . await ?) ) ,
294301 Err ( err) => Err ( err) ,
295302 }
296303 }
@@ -310,12 +317,13 @@ impl PrismDatasetRequest {
310317 & self ,
311318 stream : String ,
312319 info : PrismLogstreamInfo ,
320+ key : & SessionKey ,
313321 ) -> Result < PrismDatasetResponse , PrismLogstreamError > {
314322 // Get hot tier info
315323 let hottier = self . get_hot_tier_info ( & stream) . await ?;
316324
317325 // Get counts
318- let counts = self . get_counts ( & stream) . await ?;
326+ let counts = self . get_counts ( & stream, key ) . await ?;
319327
320328 Ok ( PrismDatasetResponse {
321329 stream,
@@ -344,20 +352,84 @@ impl PrismDatasetRequest {
344352 }
345353 }
346354
347- async fn get_counts ( & self , stream : & str ) -> Result < CountsResponse , PrismLogstreamError > {
355+ async fn get_counts (
356+ & self ,
357+ stream : & str ,
358+ key : & SessionKey ,
359+ ) -> Result < CountsResponse , PrismLogstreamError > {
360+ let end = truncate_to_minute ( Utc :: now ( ) ) ;
361+ let start = end - TimeDelta :: hours ( 1 ) ;
362+
363+ let conditions = if PARSEABLE . get_stream ( stream) ?. get_time_partition ( ) . is_some ( ) {
364+ Some ( CountConditions {
365+ conditions : Some ( Conditions {
366+ operator : Some ( crate :: alerts:: LogicalOperator :: And ) ,
367+ condition_config : vec ! [
368+ ConditionConfig {
369+ column: DEFAULT_TIMESTAMP_KEY . into( ) ,
370+ operator: crate :: alerts:: WhereConfigOperator :: GreaterThanOrEqual ,
371+ value: Some ( start. to_rfc3339( ) ) ,
372+ } ,
373+ ConditionConfig {
374+ column: DEFAULT_TIMESTAMP_KEY . into( ) ,
375+ operator: crate :: alerts:: WhereConfigOperator :: LessThan ,
376+ value: Some ( end. to_rfc3339( ) ) ,
377+ } ,
378+ ] ,
379+ } ) ,
380+ group_by : None ,
381+ } )
382+ } else {
383+ None
384+ } ;
385+
348386 let count_request = CountsRequest {
349387 stream : stream. to_owned ( ) ,
350- start_time : "1h" . to_owned ( ) ,
351- end_time : "now" . to_owned ( ) ,
388+ start_time : start . to_rfc3339 ( ) ,
389+ end_time : end . to_rfc3339 ( ) ,
352390 num_bins : 10 ,
353- conditions : None ,
391+ conditions,
354392 } ;
355393
356- let records = count_request. get_bin_density ( ) . await ?;
357- Ok ( CountsResponse {
358- fields : vec ! [ "start_time" . into( ) , "end_time" . into( ) , "count" . into( ) ] ,
359- records,
360- } )
394+ if count_request. conditions . is_some ( ) {
395+ // forward request to querier
396+ let query = count_request
397+ . get_df_sql ( DEFAULT_TIMESTAMP_KEY . into ( ) )
398+ . await ?;
399+
400+ let query_request = Query {
401+ query,
402+ start_time : start. to_rfc3339 ( ) ,
403+ end_time : end. to_rfc3339 ( ) ,
404+ send_null : true ,
405+ fields : true ,
406+ streaming : false ,
407+ filter_tags : None ,
408+ } ;
409+
410+ let ( records, _) = get_records_and_fields ( & query_request, key) . await ?;
411+ if let Some ( records) = records {
412+ let json_records = record_batches_to_json ( & records) ?;
413+ let records = json_records. into_iter ( ) . map ( Value :: Object ) . collect_vec ( ) ;
414+
415+ let res = json ! ( {
416+ "fields" : vec![ "start_time" , "end_time" , "count" ] ,
417+ "records" : records,
418+ } ) ;
419+
420+ Ok ( serde_json:: from_value ( res) ?)
421+ } else {
422+ Err ( PrismLogstreamError :: Anyhow ( anyhow:: Error :: msg (
423+ "No data returned for counts SQL" ,
424+ ) ) )
425+ }
426+ } else {
427+ let records = count_request. get_bin_density ( ) . await ?;
428+ Ok ( CountsResponse {
429+ fields : vec ! [ "start_time" . into( ) , "end_time" . into( ) , "count" . into( ) ] ,
430+ records,
431+ } )
432+ }
361433 }
362434}
363435
@@ -379,6 +451,10 @@ pub enum PrismLogstreamError {
379451 Execute ( #[ from] ExecuteError ) ,
380452 #[ error( "Auth: {0}" ) ]
381453 Auth ( #[ from] actix_web:: Error ) ,
454+ #[ error( "SerdeError: {0}" ) ]
455+ SerdeError ( #[ from] serde_json:: Error ) ,
456+ #[ error( "ReqwestError: {0}" ) ]
457+ ReqwestError ( #[ from] reqwest:: Error ) ,
382458}
383459
384460impl actix_web:: ResponseError for PrismLogstreamError {
@@ -391,6 +467,8 @@ impl actix_web::ResponseError for PrismLogstreamError {
391467 PrismLogstreamError :: Query ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
392468 PrismLogstreamError :: TimeParse ( _) => StatusCode :: NOT_FOUND ,
393469 PrismLogstreamError :: Execute ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
470+ PrismLogstreamError :: SerdeError ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
471+ PrismLogstreamError :: ReqwestError ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
394472 PrismLogstreamError :: Auth ( _) => StatusCode :: UNAUTHORIZED ,
395473 }
396474 }
0 commit comments