@@ -20,28 +20,35 @@ use std::sync::Arc;
20
20
21
21
use actix_web:: http:: header:: ContentType ;
22
22
use arrow_schema:: Schema ;
23
- use chrono:: Utc ;
23
+ use chrono:: { TimeDelta , Utc } ;
24
24
use http:: StatusCode ;
25
+ use itertools:: Itertools ;
25
26
use serde:: { Deserialize , Serialize } ;
27
+ use serde_json:: { Value , json} ;
26
28
use tracing:: warn;
27
29
28
30
use crate :: {
29
31
LOCK_EXPECT ,
32
+ alerts:: alert_structs:: { ConditionConfig , Conditions } ,
33
+ event:: DEFAULT_TIMESTAMP_KEY ,
30
34
handlers:: http:: {
31
35
cluster:: {
32
36
fetch_stats_from_ingestors,
33
37
utils:: { IngestionStats , QueriedStats , StorageStats , merge_queried_stats} ,
34
38
} ,
35
39
logstream:: error:: StreamError ,
36
- query:: { QueryError , update_schema_when_distributed} ,
40
+ query:: { Query , QueryError , get_records_and_fields , update_schema_when_distributed} ,
37
41
} ,
38
42
hottier:: { HotTierError , HotTierManager , StreamHotTier } ,
39
43
parseable:: { PARSEABLE , StreamNotFound } ,
40
- query:: { CountsRequest , CountsResponse , error:: ExecuteError } ,
44
+ query:: { CountConditions , CountsRequest , CountsResponse , error:: ExecuteError } ,
41
45
rbac:: { Users , map:: SessionKey , role:: Action } ,
42
46
stats,
43
47
storage:: { StreamInfo , StreamType , retention:: Retention } ,
44
- utils:: time:: TimeParseError ,
48
+ utils:: {
49
+ arrow:: record_batches_to_json,
50
+ time:: { TimeParseError , truncate_to_minute} ,
51
+ } ,
45
52
validator:: error:: HotTierValidationError ,
46
53
} ;
47
54
@@ -218,7 +225,7 @@ pub struct PrismDatasetResponse {
218
225
219
226
/// Request parameters for retrieving Prism dataset information.
220
227
/// Defines which streams to query
221
- #[ derive( Deserialize , Default ) ]
228
+ #[ derive( Deserialize , Default , Serialize ) ]
222
229
#[ serde( rename_all = "camelCase" ) ]
223
230
pub struct PrismDatasetRequest {
224
231
/// List of stream names to query
@@ -292,7 +299,7 @@ impl PrismDatasetRequest {
292
299
293
300
// Process stream data
294
301
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 ?) ) ,
296
303
Err ( err) => Err ( err) ,
297
304
}
298
305
}
@@ -312,12 +319,13 @@ impl PrismDatasetRequest {
312
319
& self ,
313
320
stream : String ,
314
321
info : PrismLogstreamInfo ,
322
+ key : & SessionKey ,
315
323
) -> Result < PrismDatasetResponse , PrismLogstreamError > {
316
324
// Get hot tier info
317
325
let hottier = self . get_hot_tier_info ( & stream) . await ?;
318
326
319
327
// Get counts
320
- let counts = self . get_counts ( & stream) . await ?;
328
+ let counts = self . get_counts ( & stream, key ) . await ?;
321
329
322
330
Ok ( PrismDatasetResponse {
323
331
stream,
@@ -346,20 +354,84 @@ impl PrismDatasetRequest {
346
354
}
347
355
}
348
356
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
+
350
388
let count_request = CountsRequest {
351
389
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 ( ) ,
354
392
num_bins : 10 ,
355
- conditions : None ,
393
+ conditions,
356
394
} ;
357
395
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
+ }
363
435
}
364
436
}
365
437
@@ -381,6 +453,10 @@ pub enum PrismLogstreamError {
381
453
Execute ( #[ from] ExecuteError ) ,
382
454
#[ error( "Auth: {0}" ) ]
383
455
Auth ( #[ from] actix_web:: Error ) ,
456
+ #[ error( "SerdeError: {0}" ) ]
457
+ SerdeError ( #[ from] serde_json:: Error ) ,
458
+ #[ error( "ReqwestError: {0}" ) ]
459
+ ReqwestError ( #[ from] reqwest:: Error ) ,
384
460
}
385
461
386
462
impl actix_web:: ResponseError for PrismLogstreamError {
@@ -393,6 +469,8 @@ impl actix_web::ResponseError for PrismLogstreamError {
393
469
PrismLogstreamError :: Query ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
394
470
PrismLogstreamError :: TimeParse ( _) => StatusCode :: NOT_FOUND ,
395
471
PrismLogstreamError :: Execute ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
472
+ PrismLogstreamError :: SerdeError ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
473
+ PrismLogstreamError :: ReqwestError ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
396
474
PrismLogstreamError :: Auth ( _) => StatusCode :: UNAUTHORIZED ,
397
475
}
398
476
}
0 commit comments