@@ -342,6 +342,106 @@ mod tests {
342342 assert_eq ! ( vector. values[ 0 ] . value, hll( 0 ..2_000 ) . estimate( ) ) ;
343343 }
344344
345+ /// A SQL quantile served by rolling up a DDSketch grouped by
346+ /// (endpoint, service), over a lookback of several tumbling windows: the
347+ /// shape `--planner saved` writes for sketch-bench's p99 CUBE.
348+ #[ test]
349+ fn sql_quantile_by_subset_over_several_windows_equals_the_merged_sketch ( ) {
350+ const WINDOW_MS : u64 = 60_000 ;
351+ const WINDOWS : u64 = 5 ;
352+ // A multiple of the window, as the e2e client floors eval times.
353+ const ALIGNED_QUERY_TIME_S : f64 = 960.0 ;
354+ let sql = "SELECT service, QUANTILE(0.9, value) FROM events WHERE time BETWEEN \
355+ DATEADD(s, -300, NOW()) AND NOW() GROUP BY service";
356+ let streaming_config = Arc :: new ( StreamingConfig {
357+ aggregation_configs : HashMap :: from ( [ (
358+ 1 ,
359+ AggregationConfig {
360+ aggregation_id : 1 ,
361+ aggregation_type : AggregationType :: DDSketch ,
362+ aggregation_sub_type : String :: new ( ) ,
363+ parameters : HashMap :: new ( ) ,
364+ grouping_labels : KeyByLabelNames :: new ( vec ! [
365+ "endpoint" . to_string( ) ,
366+ "service" . to_string( ) ,
367+ ] ) ,
368+ aggregated_labels : KeyByLabelNames :: empty ( ) ,
369+ rollup_labels : KeyByLabelNames :: empty ( ) ,
370+ original_yaml : String :: new ( ) ,
371+ window_size_ms : WINDOW_MS ,
372+ slide_interval_ms : WINDOW_MS ,
373+ window_type : WindowType :: Tumbling ,
374+ spatial_filter : String :: new ( ) ,
375+ spatial_filter_normalized : String :: new ( ) ,
376+ metric : "events" . to_string ( ) ,
377+ num_aggregates_to_retain : None ,
378+ read_count_threshold : None ,
379+ table_name : Some ( "events" . to_string ( ) ) ,
380+ value_column : Some ( "value" . to_string ( ) ) ,
381+ } ,
382+ ) ] ) ,
383+ } ) ;
384+ let store = Arc :: new ( SimpleMapStore :: new (
385+ streaming_config. clone ( ) ,
386+ CleanupPolicy :: NoCleanup ,
387+ ) ) ;
388+ let query_time_ms = ( ALIGNED_QUERY_TIME_S * 1_000.0 ) as u64 ;
389+ let mut all = Vec :: new ( ) ;
390+ for ( g, labels) in [ [ "/x" , "a" ] , [ "/y" , "a" ] ] . iter ( ) . enumerate ( ) {
391+ for w in 0 ..WINDOWS {
392+ let values: Vec < f64 > = ( 1 ..=100 )
393+ . map ( |i| f64:: from ( i + 100 * ( w as u32 * 2 + g as u32 ) ) )
394+ . collect ( ) ;
395+ all. extend ( values. iter ( ) . copied ( ) ) ;
396+ let end = query_time_ms - w * WINDOW_MS ;
397+ store
398+ . insert_precomputed_output (
399+ PrecomputedOutput :: new (
400+ end - WINDOW_MS ,
401+ end,
402+ key ( labels) . map ( |labels| KeyByLabelValues { labels } ) ,
403+ 1 ,
404+ ) ,
405+ Box :: new ( dd ( values) ) ,
406+ )
407+ . unwrap ( ) ;
408+ }
409+ }
410+ let inference_config = InferenceConfig {
411+ schema : SchemaConfig :: SQL ( create_test_schema (
412+ "events" ,
413+ "time" ,
414+ "value" ,
415+ vec ! [ "endpoint" , "service" ] ,
416+ ) ) ,
417+ query_configs : vec ! [ QueryConfig :: new( sql. to_string( ) )
418+ . add_aggregation( AggregationReference :: new( 1 , None ) ) ] ,
419+ cleanup_policy : CleanupPolicy :: NoCleanup ,
420+ } ;
421+ let engine = SimpleEngine :: new (
422+ store,
423+ inference_config,
424+ streaming_config,
425+ 1_000 ,
426+ QueryLanguage :: sql,
427+ ) ;
428+
429+ let ( _, result) = engine
430+ . handle_query_sql ( sql. to_string ( ) , ALIGNED_QUERY_TIME_S )
431+ . expect ( "the SQL roll-up should be served locally" ) ;
432+ let QueryResult :: Vector ( vector) = result else {
433+ panic ! ( "expected an instant vector" ) ;
434+ } ;
435+ let expected = dd ( all) . get_quantile ( 0.9 ) ;
436+ assert_eq ! ( vector. values. len( ) , 1 ) ;
437+ assert_eq ! ( vector. values[ 0 ] . labels. labels, vec![ "a" . to_string( ) ] ) ;
438+ assert ! (
439+ ( vector. values[ 0 ] . value - expected) . abs( ) <= expected * 1e-9 ,
440+ "rolled-up p90 {} should equal the whole-data sketch's {expected}" ,
441+ vector. values[ 0 ] . value
442+ ) ;
443+ }
444+
345445 /// A range query rolls up at every step, and a subgroup that only
346446 /// appears partway through contributes from then on.
347447 #[ test]
0 commit comments