From ea7ee2c40bf030602e4a2d33fb5ae6304e60f8ef Mon Sep 17 00:00:00 2001 From: zzylol <50204836+zzylol@users.noreply.github.com> Date: Mon, 5 Oct 2026 02:48:43 +0000 Subject: [PATCH 1/3] feat(stage3): price TimeShift as free and TimeRange by the rows it keeps (Q66) Stage 3 charged every query-time TimeShift (as a pass-through) and every query-time TimeRange (as a filter) one operation per row of the scan below it. Over Example 3a's shared 5-year scan each shift and range cost as much as the scan's rows, so the shared-segment plan (10 of them) lost to the shared-input plan (8). A TimeShift now costs nothing at either timing: it only re-labels time, and the executor folds the offset into the time bounds of the read below it. A query-time TimeRange is charged one operation per row it keeps; at ingestion time it keeps every arriving row, so nothing changes there. Pane roles are unchanged. Example 3a and 4a now select the shared 1-year segments (18,104/s vs 25,112/s for shared input in 3a). Example 3b/4 Pattern B's query-time panes now take 130 ms per evaluation, within the 200 ms bound, so B2 is valid (still costlier than the selected plan). Example 1's selection is unchanged; its plans with tumbling panes get cheaper. Co-Authored-By: Claude Opus 5.5 --- .../tests/planner_layering_example3.rs | 18 +- .../tests/planner_layering_example4.rs | 14 +- crates/plan-selection/src/lib.rs | 478 ++++--- .../proposals/stage3-cost-model.md | 16 +- .../examples/planner-layering-example1.json | 1160 ++++++++--------- 5 files changed, 885 insertions(+), 801 deletions(-) diff --git a/crates/integration-tests/tests/planner_layering_example3.rs b/crates/integration-tests/tests/planner_layering_example3.rs index 824efb26f..2eec9dfad 100644 --- a/crates/integration-tests/tests/planner_layering_example3.rs +++ b/crates/integration-tests/tests/planner_layering_example3.rs @@ -498,10 +498,12 @@ fn stage3_b_selects_cheapest_valid() { /// Merged KLL and DDSketch panes keep the family's guarantee, so Stage 3 /// finds no tumbling candidate inaccurate. Rebuilding all five panes at -/// every evaluation takes 310 ms of query-time work, over the 200 ms latency -/// bound (S6); with the panes maintained at ingestion time, only the merge -/// and the estimate remain and the candidate is valid and priced. Panes -/// kept at query time (B3) need a capability the executor lacks. +/// every evaluation takes 130 ms of query-time work, within the 200 ms +/// latency bound (S6), now that the panes' time shifts are free and each +/// 1-min range pays only for its own rows (Q66); with the panes maintained at +/// ingestion time, only the merge and the estimate remain. Both are valid +/// and priced. Panes kept at query time (B3) need a capability the executor +/// lacks. #[test] fn stage3_b_tumbling_candidates_are_valid() { let run = run_b(); @@ -526,14 +528,6 @@ fn stage3_b_tumbling_candidates_are_valid() { p.id, invalid.get(p.id.as_str()) ), - "" => assert!( - invalid - .get(p.id.as_str()) - .is_some_and(|r| r.contains("310.0 ms") && r.contains("200 ms latency")), - "{}: {:?}", - p.id, - invalid.get(p.id.as_str()) - ), _ => { assert!( !invalid.contains_key(p.id.as_str()), diff --git a/crates/integration-tests/tests/planner_layering_example4.rs b/crates/integration-tests/tests/planner_layering_example4.rs index 76e6aaa95..541eafd66 100644 --- a/crates/integration-tests/tests/planner_layering_example4.rs +++ b/crates/integration-tests/tests/planner_layering_example4.rs @@ -406,16 +406,18 @@ fn b1_b2(run: &Run, every_min: u64) -> (f64, f64) { ) } -/// At Pattern B's 1M series, rebuilding the five panes at query time breaks -/// the 200 ms latency bound, so B2 is invalid and B1 is priced. +/// At Pattern B's 1M series, rebuilding the five panes at query time takes +/// 130 ms, within the 200 ms latency bound: the panes' time shifts are free +/// and each 1-min range pays only for its own rows (Q66). So B2 and B1 are +/// both valid and priced, and B2, which keeps nothing, is cheaper. #[test] -fn stage3_b_rebuilding_a_million_series_breaks_the_latency_bound() { +fn stage3_b_rebuilding_a_million_series_meets_the_latency_bound() { let run = run_promql(&pattern_b()); let options = options_of(&run, tumbling(), 1); let b2 = &options[&NotMaterialized].0; - let reason = run.invalid()[b2.as_str()]; - assert!(reason.contains("latency bound"), "{b2}: {reason}"); - assert!(run.cost(&options[&IngestionTime].0).is_some()); + assert!(!run.invalid().contains_key(b2.as_str()), "{b2}"); + let (b1, b2) = b1_b2(&run, 1); + assert!(b2 < b1, "B2 {b2} vs B1 {b1}"); } /// When the deployment keeps raw data, B2 pays only to read it, while B1 diff --git a/crates/plan-selection/src/lib.rs b/crates/plan-selection/src/lib.rs index 834abdebc..a6297f672 100644 --- a/crates/plan-selection/src/lib.rs +++ b/crates/plan-selection/src/lib.rs @@ -63,10 +63,8 @@ use asap_types::ir::NonASAPOp; use std::collections::{BTreeMap, HashMap}; use std::rc::Rc; +use asap_types::ir::physical_export::{ PhysicalASAPDAG, PhysicalASAPNodeId, PhysicalASAPOperatorPayload as Payload}; use asap_types::ir::operator::Reduction; -use asap_types::ir::physical_export::{ - PhysicalASAPDAG, PhysicalASAPNodeId, PhysicalASAPOperatorPayload as Payload, -}; use asap_types::ir::schema::{DataType, Schema}; use asap_types::ir::schema::{ FieldDataType, SketchAlgorithm, SketchParams, SketchStatistic, WeightDomain, @@ -1425,15 +1423,13 @@ fn scan_extent_ms( .filter_map(|e| { let consumer = dag.nodes.iter().find(|n| n.id == e.consumer)?; match &consumer.payload { - Payload::NonASAP(NonASAPOp::TimeShift { shift, .. }) => scan_extent_ms( + Payload::NonASAP(NonASAPOp::TimeShift { shift }) => scan_extent_ms( dag, roles, consumer.id, offset_ms.saturating_add(shift.offset_ms), ), - Payload::NonASAP(NonASAPOp::TimeRange { range, .. }) => { - Some((range.as_millis() as u64).saturating_add(offset_ms.max(0) as u64)) - } + Payload::NonASAP(NonASAPOp::TimeRange { range, .. }) => Some((range.as_millis() as u64).saturating_add(offset_ms.max(0) as u64)), _ => None, } }) @@ -1473,7 +1469,7 @@ fn pane_roles(dag: &PhysicalASAPDAG) -> HashMap { producers(pane) .flat_map(producers) .find_map(|id| match &nodes[&id].payload { - Payload::NonASAP(NonASAPOp::TimeShift { shift, .. }) => Some(shift.offset_ms), + Payload::NonASAP(NonASAPOp::TimeShift { shift }) => Some(shift.offset_ms), _ => None, }) .unwrap_or(0) @@ -1482,12 +1478,11 @@ fn pane_roles(dag: &PhysicalASAPDAG) -> HashMap { for merge in dag .nodes .iter() - .filter(|n| matches!(n.payload, Payload::ASAP(ASAPOp::SummaryMerge { .. }))) + .filter(|n| matches!(n.payload, Payload::ASAP(ASAPOp::SummaryMerge {}))) { let panes: Vec<_> = producers(merge.id) .filter(|&id| { - materialized(id) - && matches!(nodes[&id].payload, Payload::ASAP(ASAPOp::SummaryAgg { .. })) + materialized(id) && matches!(nodes[&id].payload, Payload::ASAP(ASAPOp::SummaryAgg { .. })) }) .collect(); let Some(&newest) = panes.iter().min_by_key(|&&id| shift(id)) else { @@ -1575,7 +1570,11 @@ fn price_nodes( calibration: &Stage3Calibration, raw_bytes_per_sample: Option, ) -> Result { - let first = dag.roots.first().copied().unwrap_or(0); + let first = dag + .roots + .first() + .copied() + .unwrap_or(0); calibration.validate().map_err(|error| (first, error))?; let series = data .input_cardinality @@ -1640,211 +1639,220 @@ fn price_nodes( } }; let groups = |reduction: &Reduction| bounded_groups(reduction, input.rows); - let (out, estimate, detail) = - match &node.payload { - Payload::NonASAP(operator) => match operator { - NonASAPOp::Scan { .. } => { - // At ingestion time, one second of arriving rows; at - // query time, as far back as the ranges reading it reach. - let span_ms = match ingestion { - true => 1_000, - false => scan_extent_ms(dag, &roles, node.id, 0) - .unwrap_or(DEFAULT_LOOKBACK_MS), - }; - let out = edge(scan_rows(rows_per_ms, span_ms)); - let estimate = estimate_operator( - PhysicalOperator::Scan, - OperatorStatistics::Scan { - edges: UnaryEdgeStatistics { - input: out, - output: out, - promql: None, - }, - source_read_bytes: out.bytes, - }, - ); - (out, estimate, format!("scan {} samples", out.rows)) - } - NonASAPOp::Aggregate { - reduction, - measures, - .. - } => { - let group_count = groups(reduction); - let keys = match reduction { - Reduction::Reduce(keys) => keys.keys().len() as u64, - _ => 1, - }; - let out = edge(group_count); - let estimate = estimate_operator( - PhysicalOperator::HashAggregate { - grouping_key_count: keys, - accumulator_count: measures.len().max(1) as u64, - }, - OperatorStatistics::HashAggregate { - edges: unary(out), - group_count, - key_bytes: 16 * keys, - accumulator_bytes_per_group: 8, - }, - ); - ( - out, - estimate, - format!( - "hash aggregate {} rows into {group_count} groups", - input.rows - ), - ) - } - NonASAPOp::Sort { - keys, partition_by, .. - } => { - let partitions = if partition_by.keys().is_empty() { - 1 - } else { - DEFAULT_GROUP_COUNT.min(input.rows.max(1)) - }; - let estimate = estimate_operator( - PhysicalOperator::InMemoryComparisonSort { - ordering_key_count: keys.len() as u64, - partitioned: partitions > 1, - }, - OperatorStatistics::InMemoryComparisonSort { - edges: unary(input), - input_partitioning: split(input, partitions), - }, - ); - ( - input, - estimate, - format!("sort {} rows in {partitions} partitions", input.rows), - ) - } - NonASAPOp::Limit { - n, - offset, - partition_by, - .. - } => { - let partitions = partition_count(!partition_by.keys().is_empty()); - let limit = n.map_or(u64::MAX, |n| (n as u64).saturating_mul(partitions)); - let offset = (*offset as u64).saturating_mul(partitions); - let out = edge(selected_rows(input.rows.saturating_sub(offset), limit)); - let estimate = estimate_operator( - PhysicalOperator::Limit { limit, offset }, - OperatorStatistics::Limit { edges: unary(out) }, - ); - (out, estimate, format!("limit to {} rows", out.rows)) - } - // At query time a range keeps only its own span of a longer - // scan: a filter on the timestamp. - NonASAPOp::TimeRange { range, .. } if !ingestion => { - let rows = (rows_per_ms * range.as_millis() as f64).round() as u64; - let out = edge(input.rows.min(rows.max(1))); - let estimate = estimate_operator( - PhysicalOperator::Filter { - predicate_operations_per_row: 1, - }, - OperatorStatistics::Filter { edges: unary(out) }, - ); - ( - out, - estimate, - format!("time range {range:?}: pass {} rows", out.rows), - ) - } - other => { - let estimate = estimate_operator( - PhysicalOperator::PassThrough, - OperatorStatistics::PassThrough { - edges: unary(input), + let (out, estimate, detail) = match &node.payload { + Payload::NonASAP(operator) => match operator { + NonASAPOp::Scan { .. } => { + // At ingestion time, one second of arriving rows; at + // query time, as far back as the ranges reading it reach. + let span_ms = match ingestion { + true => 1_000, + false => { + scan_extent_ms(dag, &roles, node.id, 0).unwrap_or(DEFAULT_LOOKBACK_MS) + } + }; + let out = edge(scan_rows(rows_per_ms, span_ms)); + let estimate = estimate_operator( + PhysicalOperator::Scan, + OperatorStatistics::Scan { + edges: UnaryEdgeStatistics { + input: out, + output: out, + promql: None, }, - ); - let name = match other { - NonASAPOp::TimeRange { range, .. } => format!("time range {range:?}"), - _ => "operator".into(), - }; - (input, estimate, format!("{name}: pass {} rows", input.rows)) - } - }, - Payload::ASAP(ASAPOp::SummaryAgg { - family, reduction, .. - }) => { + source_read_bytes: out.bytes, + }, + ); + (out, estimate, format!("scan {} samples", out.rows)) + } + NonASAPOp::Aggregate { + reduction, + measures, + .. + } => { let group_count = groups(reduction); - let (depth, state_bytes) = summary_shape(family); - let out = EdgeStatistics { - rows: group_count, - bytes: group_count * state_bytes, + let keys = match reduction { + Reduction::Reduce(keys) => keys.keys().len() as u64, + _ => 1, }; - let ops = input.rows as f64 * depth as f64; + let out = edge(group_count); + let estimate = estimate_operator( + PhysicalOperator::HashAggregate { + grouping_key_count: keys, + accumulator_count: measures.len().max(1) as u64, + }, + OperatorStatistics::HashAggregate { + edges: unary(out), + group_count, + key_bytes: 16 * keys, + accumulator_bytes_per_group: 8, + }, + ); ( out, - Ok(ResourceEstimate::new(ops, out.bytes, 0)), + estimate, format!( - "build {} into {group_count} states: {} rows x depth {depth}", - family_name(family), + "hash aggregate {} rows into {group_count} groups", input.rows ), ) } - Payload::ASAP(ASAPOp::SummaryEstimate { query, .. }) => { - let rows = match query { - // The logical result, as an exact Sort → Limit sizes it. - // Items are at most the series: a whole-expression - // sketch reads several samples per ranked item. - SketchStatistic::TopK { k } => { - let (summarized, grouped) = summarized_rows(dag, &output, node.id); - selected_rows( - summarized.min(shape.series), - (*k as u64).saturating_mul(partition_count(grouped)), - ) - } - _ => input.rows, + NonASAPOp::Sort { keys, partition_by } => { + let partitions = if partition_by.keys().is_empty() { + 1 + } else { + DEFAULT_GROUP_COUNT.min(input.rows.max(1)) }; - let out = edge(rows); + let estimate = estimate_operator( + PhysicalOperator::InMemoryComparisonSort { + ordering_key_count: keys.len() as u64, + partitioned: partitions > 1, + }, + OperatorStatistics::InMemoryComparisonSort { + edges: unary(input), + input_partitioning: split(input, partitions), + }, + ); ( - out, - Ok(ResourceEstimate::new(out.rows as f64, 0, 0)), - format!("estimate {} rows from {} states", out.rows, input.rows), + input, + estimate, + format!("sort {} rows in {partitions} partitions", input.rows), ) } - // One merge per input state. The output holds the union of the - // inputs' groups, bounded as one build over all their rows is, - // so readers see what they would see over the whole window. - Payload::ASAP(ASAPOp::SummaryMerge { .. }) => { - let merged: u64 = inputs.iter().map(|edge| edge.rows).sum(); - let reduction = dag.edges.iter().filter(|e| e.consumer == node.id).find_map( - |e| match &nodes[&e.producer].payload { - Payload::ASAP(ASAPOp::SummaryAgg { reduction, .. }) => Some(reduction), - _ => None, - }, + NonASAPOp::Limit { + n, + offset, + partition_by, + } => { + let partitions = partition_count(!partition_by.keys().is_empty()); + let limit = n.map_or(u64::MAX, |n| (n as u64).saturating_mul(partitions)); + let offset = (*offset as u64).saturating_mul(partitions); + let out = edge(selected_rows(input.rows.saturating_sub(offset), limit)); + let estimate = estimate_operator( + PhysicalOperator::Limit { limit, offset }, + OperatorStatistics::Limit { edges: unary(out) }, ); - let rows = reduction.map_or(merged, |r| bounded_groups(r, merged)); - let out = EdgeStatistics { - rows, - bytes: rows * (input.bytes / input.rows.max(1)), - }; + (out, estimate, format!("limit to {} rows", out.rows)) + } + // At query time a range keeps only its own span of a longer + // scan. Rows are ordered by time, so it seeks to that span + // and is charged for the rows it keeps, not those it skips + // (Q66). + NonASAPOp::TimeRange { range, .. } if !ingestion => { + let rows = (rows_per_ms * range.as_millis() as f64).round() as u64; + let out = edge(input.rows.min(rows.max(1))); ( out, - Ok(ResourceEstimate::new(merged as f64, 0, 0)), + Ok(ResourceEstimate::new(out.rows as f64, width, 0)), format!( - "merge {merged} states from {} inputs into {rows}", - inputs.len() + "time range {range:?}: keep {} of {} rows", + out.rows, input.rows ), ) } - Payload::ASAP(ASAPOp::FinalizeExactAccumulator { .. }) => ( - edge(input.rows), - Ok(ResourceEstimate::new(input.rows as f64, 0, 0)), - format!("finalize {} accumulators", input.rows), - ), - _ => ( - edge(input.rows), - Ok(ResourceEstimate::new(input.rows as f64, 0, 0)), - format!("{} rows", input.rows), + // A shift only re-labels time: the executor folds it into + // the time bounds of the read below it, so it does no + // per-row work (Q66). + NonASAPOp::TimeShift { shift } => ( + input, + Ok(ResourceEstimate::new(0.0, 0, 0)), + format!( + "time shift {} ms: re-label {} rows, free", + shift.offset_ms, input.rows + ), ), - }; + other => { + let estimate = estimate_operator( + PhysicalOperator::PassThrough, + OperatorStatistics::PassThrough { + edges: unary(input), + }, + ); + let name = match other { + NonASAPOp::TimeRange { range, .. } => format!("time range {range:?}"), + _ => "operator".into(), + }; + (input, estimate, format!("{name}: pass {} rows", input.rows)) + } + }, + Payload::ASAP(ASAPOp::SummaryAgg { + family, reduction, .. + }) => { + let group_count = groups(reduction); + let (depth, state_bytes) = summary_shape(family); + let out = EdgeStatistics { + rows: group_count, + bytes: group_count * state_bytes, + }; + let ops = input.rows as f64 * depth as f64; + ( + out, + Ok(ResourceEstimate::new(ops, out.bytes, 0)), + format!( + "build {} into {group_count} states: {} rows x depth {depth}", + family_name(family), + input.rows + ), + ) + } + Payload::ASAP(ASAPOp::SummaryEstimate { query }) => { + let rows = match query { + // The logical result, as an exact Sort → Limit sizes it. + // Items are at most the series: a whole-expression + // sketch reads several samples per ranked item. + SketchStatistic::TopK { k } => { + let (summarized, grouped) = summarized_rows(dag, &output, node.id); + selected_rows( + summarized.min(shape.series), + (*k as u64).saturating_mul(partition_count(grouped)), + ) + } + _ => input.rows, + }; + let out = edge(rows); + ( + out, + Ok(ResourceEstimate::new(out.rows as f64, 0, 0)), + format!("estimate {} rows from {} states", out.rows, input.rows), + ) + } + // One merge per input state. The output holds the union of the + // inputs' groups, bounded as one build over all their rows is, + // so readers see what they would see over the whole window. + Payload::ASAP(ASAPOp::SummaryMerge {}) => { + let merged: u64 = inputs.iter().map(|edge| edge.rows).sum(); + let reduction = dag + .edges + .iter() + .filter(|e| e.consumer == node.id) + .find_map(|e| match &nodes[&e.producer].payload { + Payload::ASAP(ASAPOp::SummaryAgg { reduction, .. }) => Some(reduction), + _ => None, + }); + let rows = reduction.map_or(merged, |r| bounded_groups(r, merged)); + let out = EdgeStatistics { + rows, + bytes: rows * (input.bytes / input.rows.max(1)), + }; + ( + out, + Ok(ResourceEstimate::new(merged as f64, 0, 0)), + format!( + "merge {merged} states from {} inputs into {rows}", + inputs.len() + ), + ) + } + Payload::ASAP(ASAPOp::FinalizeExactAccumulator {}) => ( + edge(input.rows), + Ok(ResourceEstimate::new(input.rows as f64, 0, 0)), + format!("finalize {} accumulators", input.rows), + ), + _ => ( + edge(input.rows), + Ok(ResourceEstimate::new(input.rows as f64, 0, 0)), + format!("{} rows", input.rows), + ), + }; let cost = estimate .and_then(|estimate| estimate.calibrated_cost(&resources)) .map_err(|error| (node.id, error))?; @@ -1977,7 +1985,10 @@ fn raw_retention( .iter() .filter(|n| { n.output_state.timing.is_query_time() - && matches!(n.payload, Payload::NonASAP(NonASAPOp::Scan { .. })) + && matches!( + n.payload, + Payload::NonASAP(NonASAPOp::Scan { .. }) + ) }) .map(|n| { let span_ms = scan_extent_ms(dag, roles, n.id, 0).unwrap_or(DEFAULT_LOOKBACK_MS); @@ -2744,7 +2755,10 @@ mod tests { } fn is_scan(payload: &Payload) -> bool { - matches!(payload, Payload::NonASAP(NonASAPOp::Scan { .. })) + matches!( + payload, + Payload::NonASAP(NonASAPOp::Scan { .. }) + ) } fn is_build(payload: &Payload) -> bool { @@ -2837,8 +2851,8 @@ mod tests { .filter(|n| !n.output_state.timing.is_query_time() && cost.per_node[&n.id].cost > 0.0) .map(|n| cost.per_node[&n.id].detail.clone()) .collect(); - // The scan, one shift, one range and the newest pane. - assert_eq!(ingestion.len(), 4, "{ingestion:#?}"); + // The scan, one range and the newest pane; the shift is free (Q66). + assert_eq!(ingestion.len(), 3, "{ingestion:#?}"); } /// Kept at query time (B3, Q59), each evaluation builds the newest pane @@ -3196,6 +3210,69 @@ mod tests { assert!(shared <= separate, "shared {shared} vs separate {separate}"); } + /// Q66: over a shared 5-year scan, a time shift is free and each time + /// range is charged for the rows it keeps: the 1-year range costs a + /// fifth of the 5-year one, not the same. + #[test] + fn time_shift_is_free_and_time_range_pays_for_the_rows_it_keeps() { + let queries = [ + "quantile_over_time(0.99, latency_ms[5y])", + "quantile_over_time(0.99, latency_ms[1y] offset 2y)", + ]; + let roots = queries + .iter() + .enumerate() + .map(|(i, query)| { + let root = lower_promql(query, AccuracyTarget::Exact); + let root = + asap_types::ir::schema_support::with_promql_series_identity(&root).unwrap(); + (i, QueryRoot::Operator(root)) + }) + .collect(); + let stage1 = stage1_logical_candidates(roots, &Default::default(), &[]).unwrap(); + let demand = vec![every_10s(None); queries.len()]; + let data = data(); + let variant = *variants(&stage1) + .iter() + .find(|v| v.sharing == Sharing::IdenticalExpressions) + .unwrap(); + let raw = vec![0; variant.inventory.targets.len()]; + let models = PlanningModels::builtin(); + let (_, mut stage2) = realize(variant, &raw, &demand, &data, &models).unwrap(); + let candidate = stage2.candidates.swap_remove(0); + let cost = assess(&candidate, &demand, &data, &models).unwrap(); + let of = |pick: fn(&NonASAPOpKind) -> bool| -> Vec<&NodeCost> { + candidate + .dag + .nodes + .iter() + .filter( + |n| matches!(&n.payload, Payload::NonASAP(operator) if pick(operator)), + ) + .map(|n| &cost.per_node[&n.id]) + .collect() + }; + let shifts = of(|op| matches!(op, NonASAPOp::TimeShift { .. })); + assert_eq!(shifts.len(), 1); + assert_eq!(shifts[0].cost, 0.0, "{}", shifts[0].detail); + let scan = of(|op| matches!(op, NonASAPOp::Scan { .. }))[0].rows; + let mut ranges = of(|op| matches!(op, NonASAPOp::TimeRange { .. })); + ranges.sort_by_key(|n| n.rows); + let [year, five_years] = ranges.as_slice() else { + panic!("two time ranges"); + }; + assert_eq!(five_years.rows, scan); + assert!((five_years.rows as f64 / year.rows as f64 - 5.0).abs() < 0.01); + // Charged per kept row: the same price per row for both. + let per_row = |n: &NodeCost| n.cost / n.rows as f64; + assert!((per_row(year) / per_row(five_years) - 1.0).abs() < 1e-9); + assert!( + year.detail.contains(&format!("of {scan} rows")), + "{}", + year.detail + ); + } + // ── Deployment capabilities (C3, Q48) ──────────────────────────────── fn reasons(selection: &Selection) -> BTreeMap<&str, &str> { @@ -3312,8 +3389,7 @@ mod tests { let (p, _) = maintained_panes(&run); let p = p.clone(); // data(): 10 000 series; the newest pane keeps itself and 5 others. - let Payload::ASAP(ASAPOp::SummaryAgg { family, .. }) = &node_of(&p.dag, is_build).payload - else { + let Payload::ASAP(ASAPOp::SummaryAgg { family, .. }) = &node_of(&p.dag, is_build).payload else { unreachable!() }; let retained = 6 * 10_000 * summary_shape(family).1; diff --git a/docs/design_docs/proposals/stage3-cost-model.md b/docs/design_docs/proposals/stage3-cost-model.md index 75c99f209..6f0cbf3d1 100644 --- a/docs/design_docs/proposals/stage3-cost-model.md +++ b/docs/design_docs/proposals/stage3-cost-model.md @@ -53,6 +53,18 @@ the longest range plus its offset. Each range then passes only its own span. For example, `x[1y] offset 2y` scans 3 years, and a 1-year range over a shared 5-year scan passes 1 year of rows. +**Time shifts and ranges (Q66).** A time shift only re-labels time: the +executor folds the offset into the time bounds of the read below it, so a +shift is free at either timing. A query-time time range is charged one +operation per row it keeps, not per row of the scan below it: rows are +ordered by time, so it seeks to its span. The 1-year range above costs 1 +year of rows, not 5. (At ingestion time a range keeps every arriving row, so +both counts agree.) Without this, every shifted window over a shared long +scan paid for the whole scan twice, and Example 3a's shared 1-year segments +(18 104 per second; scan 12 264, five ranges and five builds of 584 each) +lost to the shared-input plan (25 112), which reads the same scan with fewer +shifts and ranges. + **Workload cost** is the sum over the candidate's DAG nodes: `cost(P) = Σ_n cost(n)`, in cost per second. @@ -292,8 +304,8 @@ candidate scales by the same factor, so their ranking is unchanged. **With materialization.** Stage 2 also offers Q2's exact `sum_over_time` in six 10-s panes maintained at ingestion time. The cheapest such candidate costs -47.407 per second: the panes' ingestion work is small (scan 0.387, shift and -range 0.133, newest-pane build 0.067), but the newest pane retains seven +47.340 per second: the panes' ingestion work is small (scan 0.387, range +0.067, the shift free, newest-pane build 0.067), but the newest pane retains seven panes of 1 000 000 per-series sums, 336 MB, for 42.0 per second of memory. The selection is unchanged. Q2's 100 ms latency bound rejects 40 candidates, every Count-Sketch + heap among them. diff --git a/tools/dag-viewer/examples/planner-layering-example1.json b/tools/dag-viewer/examples/planner-layering-example1.json index fae670f04..541412bce 100644 --- a/tools/dag-viewer/examples/planner-layering-example1.json +++ b/tools/dag-viewer/examples/planner-layering-example1.json @@ -421220,7 +421220,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -421236,7 +421236,7 @@ }, "5": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "6": { "cost": 0.8, @@ -421263,7 +421263,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -421279,7 +421279,7 @@ }, "5": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "6": { "cost": 3.2, @@ -421302,7 +421302,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -421322,7 +421322,7 @@ }, "6": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 0.8, @@ -421349,7 +421349,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0001, @@ -421373,7 +421373,7 @@ }, "6": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 0.4, @@ -421400,47 +421400,47 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "11": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "12": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "13": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "14": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "15": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "16": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "17": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "18": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "19": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -421451,12 +421451,12 @@ "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "21": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "22": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "23": { "cost": 0.06666670000000001, @@ -421491,24 +421491,24 @@ "detail": "scan 4000000 samples; x 0.1000 evaluations/s" }, "6": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "7": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "8": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "9": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 12.840100400000006, + "total": 8.440100599999997, "unit": "cost_per_second" }, "P14-m1": { @@ -421519,7 +421519,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -421610,8 +421610,8 @@ "detail": "scan 66667 samples; ingestion time" }, "6": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "7": { "cost": 0.06666699999999999, @@ -421627,7 +421627,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 48.10676960000001, + "total": 48.040102600000004, "unit": "cost_per_second" }, "P15": { @@ -421638,7 +421638,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -421658,7 +421658,7 @@ }, "6": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 0.8, @@ -421685,7 +421685,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0001, @@ -421709,7 +421709,7 @@ }, "6": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 0.4, @@ -421736,47 +421736,47 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "11": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "12": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "13": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "14": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "15": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "16": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "17": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "18": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "19": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -421787,12 +421787,12 @@ "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "21": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "22": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "23": { "cost": 0.06666670000000001, @@ -421827,24 +421827,24 @@ "detail": "scan 4000000 samples; x 0.1000 evaluations/s" }, "6": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "7": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "8": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "9": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 12.240100400000006, + "total": 7.840100599999998, "unit": "cost_per_second" }, "P17-m1": { @@ -421855,7 +421855,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -421946,8 +421946,8 @@ "detail": "scan 66667 samples; ingestion time" }, "6": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "7": { "cost": 0.06666699999999999, @@ -421963,7 +421963,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 47.506769600000005, + "total": 47.4401026, "unit": "cost_per_second" }, "P2": { @@ -421974,7 +421974,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -421990,7 +421990,7 @@ }, "5": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "6": { "cost": 0.4, @@ -422021,7 +422021,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -422041,7 +422041,7 @@ }, "6": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 3.2, @@ -422064,7 +422064,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -422084,7 +422084,7 @@ }, "6": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 0.8, @@ -422111,7 +422111,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0001, @@ -422135,7 +422135,7 @@ }, "6": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 0.4, @@ -422162,47 +422162,47 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "11": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "12": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "13": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "14": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "15": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "16": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "17": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "18": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "19": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -422213,12 +422213,12 @@ "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "21": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "22": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "23": { "cost": 0.06666670000000001, @@ -422253,24 +422253,24 @@ "detail": "scan 4000000 samples; x 0.1000 evaluations/s" }, "6": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "7": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "8": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "9": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 13.040110400000005, + "total": 8.640110599999996, "unit": "cost_per_second" }, "P25-m1": { @@ -422281,7 +422281,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -422372,8 +422372,8 @@ "detail": "scan 66667 samples; ingestion time" }, "6": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "7": { "cost": 0.06666699999999999, @@ -422389,7 +422389,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 48.306779600000006, + "total": 48.24011260000001, "unit": "cost_per_second" }, "P26": { @@ -422400,7 +422400,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -422420,7 +422420,7 @@ }, "6": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 0.8, @@ -422447,7 +422447,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0001, @@ -422471,7 +422471,7 @@ }, "6": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 0.4, @@ -422498,47 +422498,47 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "11": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "12": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "13": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "14": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "15": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "16": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "17": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "18": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "19": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -422549,12 +422549,12 @@ "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "21": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "22": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "23": { "cost": 0.06666670000000001, @@ -422589,24 +422589,24 @@ "detail": "scan 4000000 samples; x 0.1000 evaluations/s" }, "6": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "7": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "8": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "9": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 12.440110400000005, + "total": 8.040110599999997, "unit": "cost_per_second" }, "P28-m1": { @@ -422617,7 +422617,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -422708,8 +422708,8 @@ "detail": "scan 66667 samples; ingestion time" }, "6": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "7": { "cost": 0.06666699999999999, @@ -422725,7 +422725,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 47.706779600000004, + "total": 47.64011260000001, "unit": "cost_per_second" }, "P3": { @@ -422736,43 +422736,43 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "11": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "12": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "13": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "14": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "15": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "16": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "17": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "18": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "19": { "cost": 0.06666670000000001, @@ -422783,12 +422783,12 @@ "detail": "hash aggregate 4000000 rows into 1000000 groups; x 0.1000 evaluations/s" }, "20": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "21": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "22": { "cost": 0.06666670000000001, @@ -422819,28 +422819,28 @@ "detail": "scan 4000000 samples; x 0.1000 evaluations/s" }, "5": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "6": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "8": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "9": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 13.140100400000005, + "total": 8.740100599999996, "unit": "cost_per_second" }, "P3-m1": { @@ -422851,7 +422851,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -422934,8 +422934,8 @@ "detail": "scan 66667 samples; ingestion time" }, "5": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "6": { "cost": 0.06666699999999999, @@ -422955,7 +422955,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 48.406769600000004, + "total": 48.34010260000001, "unit": "cost_per_second" }, "P32": { @@ -422966,7 +422966,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -422986,7 +422986,7 @@ }, "6": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 3.2, @@ -423009,7 +423009,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0001, @@ -423037,7 +423037,7 @@ }, "7": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "8": { "cost": 0.8, @@ -423060,7 +423060,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 1.4000000000000001, @@ -423092,7 +423092,7 @@ }, "7": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "8": { "cost": 0.4, @@ -423115,67 +423115,67 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "11": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "12": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "13": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "14": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "15": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "16": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "17": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "18": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "19": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, "detail": "build exact Rate accumulator into 1000000 states: 4000000 rows x depth 1; x 0.1000 evaluations/s" }, "20": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "21": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "22": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "23": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "24": { "cost": 0.06666670000000001, @@ -423214,12 +423214,12 @@ "detail": "scan 4000000 samples; x 0.1000 evaluations/s" }, "7": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "8": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "9": { "cost": 0.06666670000000001, @@ -423227,7 +423227,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 12.740110400000006, + "total": 8.340110599999997, "unit": "cost_per_second" }, "P36-m1": { @@ -423238,7 +423238,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -423337,8 +423337,8 @@ "detail": "scan 66667 samples; ingestion time" }, "7": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "8": { "cost": 0.06666699999999999, @@ -423350,7 +423350,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 48.00677960000001, + "total": 47.940112600000006, "unit": "cost_per_second" }, "P37": { @@ -423361,7 +423361,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0001, @@ -423389,7 +423389,7 @@ }, "7": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "8": { "cost": 0.8, @@ -423412,7 +423412,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.8, @@ -423444,7 +423444,7 @@ }, "7": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "8": { "cost": 0.4, @@ -423467,67 +423467,67 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "11": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "12": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "13": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "14": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "15": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "16": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "17": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "18": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "19": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, "detail": "build exact Rate accumulator into 1000000 states: 4000000 rows x depth 1; x 0.1000 evaluations/s" }, "20": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "21": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "22": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "23": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "24": { "cost": 0.06666670000000001, @@ -423566,12 +423566,12 @@ "detail": "scan 4000000 samples; x 0.1000 evaluations/s" }, "7": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "8": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "9": { "cost": 0.06666670000000001, @@ -423579,7 +423579,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 12.140110400000006, + "total": 7.740110599999998, "unit": "cost_per_second" }, "P39-m1": { @@ -423590,7 +423590,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -423689,8 +423689,8 @@ "detail": "scan 66667 samples; ingestion time" }, "7": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "8": { "cost": 0.06666699999999999, @@ -423702,7 +423702,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 47.40677960000001, + "total": 47.340112600000005, "unit": "cost_per_second" }, "P4": { @@ -423713,7 +423713,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -423729,7 +423729,7 @@ }, "5": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "6": { "cost": 0.8, @@ -423756,7 +423756,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -423780,7 +423780,7 @@ }, "7": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "8": { "cost": 3.2, @@ -423803,7 +423803,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -423838,7 +423838,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -423877,55 +423877,55 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "11": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "12": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "13": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "14": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "15": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "16": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "17": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "18": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "19": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, "detail": "hash aggregate 4000000 rows into 1000000 groups; x 0.1000 evaluations/s" }, "20": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "21": { "cost": 0.06666670000000001, @@ -423952,24 +423952,24 @@ "detail": "hash aggregate 1000000 rows into 100 groups; x 0.1000 evaluations/s" }, "4": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "5": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "6": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "7": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "8": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "9": { "cost": 0.06666670000000001, @@ -423977,7 +423977,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 10.820100400000003, + "total": 6.420100599999998, "unit": "cost_per_second" }, "P47-m1": { @@ -423988,7 +423988,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -424071,8 +424071,8 @@ "detail": "scan 66667 samples; ingestion time" }, "5": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "6": { "cost": 0.06666699999999999, @@ -424092,7 +424092,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 48.406769600000004, + "total": 48.34010260000001, "unit": "cost_per_second" }, "P48": { @@ -424103,7 +424103,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -424138,7 +424138,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -424177,7 +424177,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -424193,7 +424193,7 @@ }, "5": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "6": { "cost": 0.4, @@ -424224,55 +424224,55 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "11": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "12": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "13": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "14": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "15": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "16": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "17": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "18": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "19": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, "detail": "hash aggregate 4000000 rows into 1000000 groups; x 0.1000 evaluations/s" }, "20": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "21": { "cost": 0.06666670000000001, @@ -424299,24 +424299,24 @@ "detail": "hash aggregate 1000000 rows into 100 groups; x 0.1000 evaluations/s" }, "4": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "5": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "6": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "7": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "8": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "9": { "cost": 0.06666670000000001, @@ -424324,7 +424324,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 10.220100400000003, + "total": 5.820100599999997, "unit": "cost_per_second" }, "P50-m1": { @@ -424335,7 +424335,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -424418,8 +424418,8 @@ "detail": "scan 66667 samples; ingestion time" }, "5": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "6": { "cost": 0.06666699999999999, @@ -424439,7 +424439,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 47.8067696, + "total": 47.74010260000001, "unit": "cost_per_second" }, "P54": { @@ -424450,7 +424450,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -424481,7 +424481,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -424520,7 +424520,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -424563,43 +424563,43 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "11": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "12": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "13": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "14": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "15": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "16": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "17": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "18": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "19": { "cost": 0.06666670000000001, @@ -424610,12 +424610,12 @@ "detail": "build exact Rate accumulator into 1000000 states: 4000000 rows x depth 1; x 0.1000 evaluations/s" }, "20": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "21": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "22": { "cost": 0.06666670000000001, @@ -424646,28 +424646,28 @@ "detail": "hash aggregate 1000000 rows into 100 groups; x 0.1000 evaluations/s" }, "5": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "6": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "8": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "9": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 10.520100400000002, + "total": 6.120100599999998, "unit": "cost_per_second" }, "P58-m1": { @@ -424678,7 +424678,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -424769,8 +424769,8 @@ "detail": "scan 66667 samples; ingestion time" }, "6": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "7": { "cost": 0.06666699999999999, @@ -424786,7 +424786,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 48.10676960000001, + "total": 48.040102600000004, "unit": "cost_per_second" }, "P59": { @@ -424797,7 +424797,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -424836,43 +424836,43 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "11": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "12": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "13": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "14": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "15": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "16": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "17": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "18": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "19": { "cost": 0.06666670000000001, @@ -424883,12 +424883,12 @@ "detail": "hash aggregate 4000000 rows into 1000000 groups; x 0.1000 evaluations/s" }, "20": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "21": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "22": { "cost": 0.06666670000000001, @@ -424919,28 +424919,28 @@ "detail": "scan 4000000 samples; x 0.1000 evaluations/s" }, "5": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "6": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "8": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "9": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 12.540100400000005, + "total": 8.140100599999997, "unit": "cost_per_second" }, "P6-m1": { @@ -424951,7 +424951,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -425034,8 +425034,8 @@ "detail": "scan 66667 samples; ingestion time" }, "5": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "6": { "cost": 0.06666699999999999, @@ -425055,7 +425055,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 47.8067696, + "total": 47.74010260000001, "unit": "cost_per_second" }, "P60": { @@ -425066,7 +425066,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -425109,43 +425109,43 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "11": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "12": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "13": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "14": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "15": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "16": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "17": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "18": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "19": { "cost": 0.06666670000000001, @@ -425156,12 +425156,12 @@ "detail": "build exact Rate accumulator into 1000000 states: 4000000 rows x depth 1; x 0.1000 evaluations/s" }, "20": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "21": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "22": { "cost": 0.06666670000000001, @@ -425192,28 +425192,28 @@ "detail": "hash aggregate 1000000 rows into 100 groups; x 0.1000 evaluations/s" }, "5": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "6": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "8": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "9": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 9.920100400000003, + "total": 5.5201005999999975, "unit": "cost_per_second" }, "P61-m1": { @@ -425224,7 +425224,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -425315,8 +425315,8 @@ "detail": "scan 66667 samples; ingestion time" }, "6": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "7": { "cost": 0.06666699999999999, @@ -425332,7 +425332,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 47.506769600000005, + "total": 47.4401026, "unit": "cost_per_second" }, "P65": { @@ -425343,7 +425343,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -425378,7 +425378,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -425417,7 +425417,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -425460,43 +425460,43 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "11": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "12": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "13": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "14": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "15": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "16": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "17": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "18": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "19": { "cost": 0.06666670000000001, @@ -425507,12 +425507,12 @@ "detail": "hash aggregate 4000000 rows into 1000000 groups; x 0.1000 evaluations/s" }, "20": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "21": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "22": { "cost": 0.06666670000000001, @@ -425543,28 +425543,28 @@ "detail": "finalize 100 accumulators; x 0.1000 evaluations/s" }, "5": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "6": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "8": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "9": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 10.720110400000003, + "total": 6.320110599999998, "unit": "cost_per_second" }, "P69-m1": { @@ -425575,7 +425575,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -425666,8 +425666,8 @@ "detail": "scan 66667 samples; ingestion time" }, "6": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "7": { "cost": 0.06666699999999999, @@ -425683,7 +425683,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 48.306779600000006, + "total": 48.24011260000001, "unit": "cost_per_second" }, "P70": { @@ -425694,7 +425694,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -425733,7 +425733,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -425776,43 +425776,43 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "11": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "12": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "13": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "14": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "15": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "16": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "17": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "18": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "19": { "cost": 0.06666670000000001, @@ -425823,12 +425823,12 @@ "detail": "hash aggregate 4000000 rows into 1000000 groups; x 0.1000 evaluations/s" }, "20": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "21": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "22": { "cost": 0.06666670000000001, @@ -425859,28 +425859,28 @@ "detail": "finalize 100 accumulators; x 0.1000 evaluations/s" }, "5": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "6": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "7": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "8": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "9": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 10.120110400000003, + "total": 5.720110599999997, "unit": "cost_per_second" }, "P72-m1": { @@ -425891,7 +425891,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -425982,8 +425982,8 @@ "detail": "scan 66667 samples; ingestion time" }, "6": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "7": { "cost": 0.06666699999999999, @@ -425999,7 +425999,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 47.706779600000004, + "total": 47.64011260000001, "unit": "cost_per_second" }, "P76": { @@ -426010,7 +426010,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.8, @@ -426045,7 +426045,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -426088,7 +426088,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -426135,47 +426135,47 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "11": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "12": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "13": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "14": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "15": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "16": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "17": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "18": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "19": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -426186,12 +426186,12 @@ "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "21": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "22": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "23": { "cost": 0.06666670000000001, @@ -426226,24 +426226,24 @@ "detail": "finalize 100 accumulators; x 0.1000 evaluations/s" }, "6": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "7": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "8": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "9": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 10.420110400000002, + "total": 6.020110599999998, "unit": "cost_per_second" }, "P80-m1": { @@ -426254,7 +426254,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -426353,8 +426353,8 @@ "detail": "scan 66667 samples; ingestion time" }, "7": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "8": { "cost": 0.06666699999999999, @@ -426366,7 +426366,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 48.00677960000001, + "total": 47.940112600000006, "unit": "cost_per_second" }, "P81": { @@ -426377,7 +426377,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -426420,7 +426420,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -426467,47 +426467,47 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "11": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "12": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 20000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "13": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "14": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "15": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 30000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "16": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "17": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "18": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 40000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "19": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -426518,12 +426518,12 @@ "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "21": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 50000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "22": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "23": { "cost": 0.06666670000000001, @@ -426558,24 +426558,24 @@ "detail": "finalize 100 accumulators; x 0.1000 evaluations/s" }, "6": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" }, "7": { - "cost": 0.4, - "detail": "time range 10s: pass 666667 rows; x 0.1000 evaluations/s" + "cost": 0.06666670000000001, + "detail": "time range 10s: keep 666667 of 4000000 rows; x 0.1000 evaluations/s" }, "8": { "cost": 0.06666670000000001, "detail": "build exact Sum accumulator into 666667 states: 666667 rows x depth 1; x 0.1000 evaluations/s" }, "9": { - "cost": 0.4, - "detail": "operator: pass 4000000 rows; x 0.1000 evaluations/s" + "cost": 0.0, + "detail": "time shift 10000 ms: re-label 4000000 rows, free; x 0.1000 evaluations/s" } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 9.820110400000003, + "total": 5.4201105999999974, "unit": "cost_per_second" }, "P83-m1": { @@ -426586,7 +426586,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "10": { "cost": 0.0, @@ -426685,8 +426685,8 @@ "detail": "scan 66667 samples; ingestion time" }, "7": { - "cost": 0.06666699999999999, - "detail": "operator: pass 66667 rows; ingestion time" + "cost": 0.0, + "detail": "time shift 0 ms: re-label 66667 rows, free; ingestion time" }, "8": { "cost": 0.06666699999999999, @@ -426698,7 +426698,7 @@ } }, "source": "analytical-cost-v2 (illustrative statistics, calibration illustrative-v2)", - "total": 47.40677960000001, + "total": 47.340112600000005, "unit": "cost_per_second" }, "P87": { @@ -426709,7 +426709,7 @@ }, "1": { "cost": 0.4, - "detail": "time range 60s: pass 4000000 rows; x 0.1000 evaluations/s" + "detail": "time range 60s: keep 4000000 of 4000000 rows; x 0.1000 evaluations/s" }, "2": { "cost": 0.4, @@ -426764,7 +426764,7 @@ }, { "id": "P9", - "reason": "q2: query-time work takes 206.2 ms per evaluation, over the 100 ms latency bound", + "reason": "q2: query-time work takes 162.2 ms per evaluation, over the 100 ms latency bound", "valid": false }, { @@ -426804,7 +426804,7 @@ }, { "id": "P20", - "reason": "q2: query-time work takes 206.2 ms per evaluation, over the 100 ms latency bound", + "reason": "q2: query-time work takes 162.2 ms per evaluation, over the 100 ms latency bound", "valid": false }, { @@ -426844,7 +426844,7 @@ }, { "id": "P31", - "reason": "q2: query-time work takes 206.2 ms per evaluation, over the 100 ms latency bound", + "reason": "q2: query-time work takes 162.2 ms per evaluation, over the 100 ms latency bound", "valid": false }, { @@ -426884,7 +426884,7 @@ }, { "id": "P42", - "reason": "q2: query-time work takes 206.2 ms per evaluation, over the 100 ms latency bound", + "reason": "q2: query-time work takes 162.2 ms per evaluation, over the 100 ms latency bound", "valid": false }, { @@ -426924,7 +426924,7 @@ }, { "id": "P53", - "reason": "q2: query-time work takes 206.2 ms per evaluation, over the 100 ms latency bound", + "reason": "q2: query-time work takes 162.2 ms per evaluation, over the 100 ms latency bound", "valid": false }, { @@ -426964,7 +426964,7 @@ }, { "id": "P64", - "reason": "q2: query-time work takes 206.2 ms per evaluation, over the 100 ms latency bound", + "reason": "q2: query-time work takes 162.2 ms per evaluation, over the 100 ms latency bound", "valid": false }, { @@ -427004,7 +427004,7 @@ }, { "id": "P75", - "reason": "q2: query-time work takes 206.2 ms per evaluation, over the 100 ms latency bound", + "reason": "q2: query-time work takes 162.2 ms per evaluation, over the 100 ms latency bound", "valid": false }, { @@ -427044,7 +427044,7 @@ }, { "id": "P86", - "reason": "q2: query-time work takes 206.2 ms per evaluation, over the 100 ms latency bound", + "reason": "q2: query-time work takes 162.2 ms per evaluation, over the 100 ms latency bound", "valid": false }, { @@ -427074,12 +427074,12 @@ }, { "id": "P3", - "reason": "costlier: 13.140 vs 4.620 cost_per_second", + "reason": "costlier: 8.740 vs 4.620 cost_per_second", "valid": true }, { "id": "P3-m1", - "reason": "costlier: 48.407 vs 4.620 cost_per_second", + "reason": "costlier: 48.340 vs 4.620 cost_per_second", "valid": true }, { @@ -427094,12 +427094,12 @@ }, { "id": "P6", - "reason": "costlier: 12.540 vs 4.620 cost_per_second", + "reason": "costlier: 8.140 vs 4.620 cost_per_second", "valid": true }, { "id": "P6-m1", - "reason": "costlier: 47.807 vs 4.620 cost_per_second", + "reason": "costlier: 47.740 vs 4.620 cost_per_second", "valid": true }, { @@ -427119,12 +427119,12 @@ }, { "id": "P14", - "reason": "costlier: 12.840 vs 4.620 cost_per_second", + "reason": "costlier: 8.440 vs 4.620 cost_per_second", "valid": true }, { "id": "P14-m1", - "reason": "costlier: 48.107 vs 4.620 cost_per_second", + "reason": "costlier: 48.040 vs 4.620 cost_per_second", "valid": true }, { @@ -427139,12 +427139,12 @@ }, { "id": "P17", - "reason": "costlier: 12.240 vs 4.620 cost_per_second", + "reason": "costlier: 7.840 vs 4.620 cost_per_second", "valid": true }, { "id": "P17-m1", - "reason": "costlier: 47.507 vs 4.620 cost_per_second", + "reason": "costlier: 47.440 vs 4.620 cost_per_second", "valid": true }, { @@ -427164,12 +427164,12 @@ }, { "id": "P25", - "reason": "costlier: 13.040 vs 4.620 cost_per_second", + "reason": "costlier: 8.640 vs 4.620 cost_per_second", "valid": true }, { "id": "P25-m1", - "reason": "costlier: 48.307 vs 4.620 cost_per_second", + "reason": "costlier: 48.240 vs 4.620 cost_per_second", "valid": true }, { @@ -427184,12 +427184,12 @@ }, { "id": "P28", - "reason": "costlier: 12.440 vs 4.620 cost_per_second", + "reason": "costlier: 8.040 vs 4.620 cost_per_second", "valid": true }, { "id": "P28-m1", - "reason": "costlier: 47.707 vs 4.620 cost_per_second", + "reason": "costlier: 47.640 vs 4.620 cost_per_second", "valid": true }, { @@ -427209,12 +427209,12 @@ }, { "id": "P36", - "reason": "costlier: 12.740 vs 4.620 cost_per_second", + "reason": "costlier: 8.340 vs 4.620 cost_per_second", "valid": true }, { "id": "P36-m1", - "reason": "costlier: 48.007 vs 4.620 cost_per_second", + "reason": "costlier: 47.940 vs 4.620 cost_per_second", "valid": true }, { @@ -427229,12 +427229,12 @@ }, { "id": "P39", - "reason": "costlier: 12.140 vs 4.620 cost_per_second", + "reason": "costlier: 7.740 vs 4.620 cost_per_second", "valid": true }, { "id": "P39-m1", - "reason": "costlier: 47.407 vs 4.620 cost_per_second", + "reason": "costlier: 47.340 vs 4.620 cost_per_second", "valid": true }, { @@ -427254,12 +427254,12 @@ }, { "id": "P47", - "reason": "costlier: 10.820 vs 4.620 cost_per_second", + "reason": "costlier: 6.420 vs 4.620 cost_per_second", "valid": true }, { "id": "P47-m1", - "reason": "costlier: 48.407 vs 4.620 cost_per_second", + "reason": "costlier: 48.340 vs 4.620 cost_per_second", "valid": true }, { @@ -427274,12 +427274,12 @@ }, { "id": "P50", - "reason": "costlier: 10.220 vs 4.620 cost_per_second", + "reason": "costlier: 5.820 vs 4.620 cost_per_second", "valid": true }, { "id": "P50-m1", - "reason": "costlier: 47.807 vs 4.620 cost_per_second", + "reason": "costlier: 47.740 vs 4.620 cost_per_second", "valid": true }, { @@ -427299,12 +427299,12 @@ }, { "id": "P58", - "reason": "costlier: 10.520 vs 4.620 cost_per_second", + "reason": "costlier: 6.120 vs 4.620 cost_per_second", "valid": true }, { "id": "P58-m1", - "reason": "costlier: 48.107 vs 4.620 cost_per_second", + "reason": "costlier: 48.040 vs 4.620 cost_per_second", "valid": true }, { @@ -427319,12 +427319,12 @@ }, { "id": "P61", - "reason": "costlier: 9.920 vs 4.620 cost_per_second", + "reason": "costlier: 5.520 vs 4.620 cost_per_second", "valid": true }, { "id": "P61-m1", - "reason": "costlier: 47.507 vs 4.620 cost_per_second", + "reason": "costlier: 47.440 vs 4.620 cost_per_second", "valid": true }, { @@ -427344,12 +427344,12 @@ }, { "id": "P69", - "reason": "costlier: 10.720 vs 4.620 cost_per_second", + "reason": "costlier: 6.320 vs 4.620 cost_per_second", "valid": true }, { "id": "P69-m1", - "reason": "costlier: 48.307 vs 4.620 cost_per_second", + "reason": "costlier: 48.240 vs 4.620 cost_per_second", "valid": true }, { @@ -427364,12 +427364,12 @@ }, { "id": "P72", - "reason": "costlier: 10.120 vs 4.620 cost_per_second", + "reason": "costlier: 5.720 vs 4.620 cost_per_second", "valid": true }, { "id": "P72-m1", - "reason": "costlier: 47.707 vs 4.620 cost_per_second", + "reason": "costlier: 47.640 vs 4.620 cost_per_second", "valid": true }, { @@ -427389,12 +427389,12 @@ }, { "id": "P80", - "reason": "costlier: 10.420 vs 4.620 cost_per_second", + "reason": "costlier: 6.020 vs 4.620 cost_per_second", "valid": true }, { "id": "P80-m1", - "reason": "costlier: 48.007 vs 4.620 cost_per_second", + "reason": "costlier: 47.940 vs 4.620 cost_per_second", "valid": true }, { @@ -427404,12 +427404,12 @@ }, { "id": "P83", - "reason": "costlier: 9.820 vs 4.620 cost_per_second", + "reason": "costlier: 5.420 vs 4.620 cost_per_second", "valid": true }, { "id": "P83-m1", - "reason": "costlier: 47.407 vs 4.620 cost_per_second", + "reason": "costlier: 47.340 vs 4.620 cost_per_second", "valid": true }, { From 16465b90f98317940b34ce865ea5ab46795de90a Mon Sep 17 00:00:00 2001 From: zzylol <50204836+zzylol@users.noreply.github.com> Date: Mon, 5 Oct 2026 03:07:32 +0000 Subject: [PATCH 2/3] docs(dag-viewer): update example stories for Q66 pricing Example 3a and 4a now select the shared 1-year segments, and 3b's query-time panes meet the latency bound. Co-Authored-By: Claude Opus 5.5 --- tools/dag-viewer/examples.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/dag-viewer/examples.json b/tools/dag-viewer/examples.json index ce6b2aa76..75857229f 100644 --- a/tools/dag-viewer/examples.json +++ b/tools/dag-viewer/examples.json @@ -18,21 +18,21 @@ "name": "3a · historical p99 batch", "example": "planner-layering-3a", "file": "out/example3a.json", - "story": "An ad hoc batch of five p99 reports over 1–5 years, run once. Each query gets a KLL, and sharing the input scan across all five is cheapest. Pass 2 also offers five 1-year KLL segments shared by all five reports, but the cost model charges each segment's time shift and range for every row of the 5-year scan, so it costs more." + "story": "An ad hoc batch of five p99 reports over 1–5 years, run once. Each query gets a KLL, and Pass 2 offers five 1-year KLL segments shared by all five reports. The segments win: each row of the 5-year scan lands in one segment, a time shift is free and a time range pays only for the rows it keeps, so they cost less than sharing only the input scan." }, { "id": "example3b", "name": "3b · live p99 panel", "example": "planner-layering-3b", "file": "out/example3b.json", - "story": "p99 over the last 5 min every minute, 1M series sampled every 15 s, raw data kept by the deployment. A per-series KLL built at query time wins: keeping 1-min KLL panes from ingestion time costs 768 cost/s of memory, and rebuilding the panes at query time breaks the 200 ms bound." + "story": "p99 over the last 5 min every minute, 1M series sampled every 15 s, raw data kept by the deployment. A per-series KLL built at query time wins: keeping 1-min KLL panes from ingestion time costs 768 cost/s of memory, and rebuilding the panes at query time meets the 200 ms bound but costs slightly more." }, { "id": "example4a", "name": "4a · monthly p99 reports", "example": "planner-layering-4a", "file": "out/example4a.json", - "story": "Example 3a repeated monthly and known in advance. The same shared-input KLL plan wins; its cost is now amortized over the month between runs. The shared 1-year segments may now be maintained at ingestion time, but keeping a million per-series KLLs per segment costs far more." + "story": "Example 3a repeated monthly and known in advance. The same shared 1-year segments win, built at query time; their cost is now amortized over the month between runs. They may now be maintained at ingestion time, but keeping a million per-series KLLs per segment costs far more." }, { "id": "example4b", From 15bc53341e54ea6d267b46336f86abedb58096a0 Mon Sep 17 00:00:00 2001 From: zzylol <50204836+zzylol@users.noreply.github.com> Date: Thu, 8 Oct 2026 15:12:14 +0000 Subject: [PATCH 3/3] refactor(plan-selection): read Operator payloads in TimeShift/TimeRange pricing Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01W7qG9aFyPij5uWsyAJCxDW --- crates/plan-selection/src/lib.rs | 427 +++++++++++++++---------------- 1 file changed, 211 insertions(+), 216 deletions(-) diff --git a/crates/plan-selection/src/lib.rs b/crates/plan-selection/src/lib.rs index a6297f672..cc17479b8 100644 --- a/crates/plan-selection/src/lib.rs +++ b/crates/plan-selection/src/lib.rs @@ -63,8 +63,10 @@ use asap_types::ir::NonASAPOp; use std::collections::{BTreeMap, HashMap}; use std::rc::Rc; -use asap_types::ir::physical_export::{ PhysicalASAPDAG, PhysicalASAPNodeId, PhysicalASAPOperatorPayload as Payload}; use asap_types::ir::operator::Reduction; +use asap_types::ir::physical_export::{ + PhysicalASAPDAG, PhysicalASAPNodeId, PhysicalASAPOperatorPayload as Payload, +}; use asap_types::ir::schema::{DataType, Schema}; use asap_types::ir::schema::{ FieldDataType, SketchAlgorithm, SketchParams, SketchStatistic, WeightDomain, @@ -1423,13 +1425,15 @@ fn scan_extent_ms( .filter_map(|e| { let consumer = dag.nodes.iter().find(|n| n.id == e.consumer)?; match &consumer.payload { - Payload::NonASAP(NonASAPOp::TimeShift { shift }) => scan_extent_ms( + Payload::NonASAP(NonASAPOp::TimeShift { shift, .. }) => scan_extent_ms( dag, roles, consumer.id, offset_ms.saturating_add(shift.offset_ms), ), - Payload::NonASAP(NonASAPOp::TimeRange { range, .. }) => Some((range.as_millis() as u64).saturating_add(offset_ms.max(0) as u64)), + Payload::NonASAP(NonASAPOp::TimeRange { range, .. }) => { + Some((range.as_millis() as u64).saturating_add(offset_ms.max(0) as u64)) + } _ => None, } }) @@ -1469,7 +1473,7 @@ fn pane_roles(dag: &PhysicalASAPDAG) -> HashMap { producers(pane) .flat_map(producers) .find_map(|id| match &nodes[&id].payload { - Payload::NonASAP(NonASAPOp::TimeShift { shift }) => Some(shift.offset_ms), + Payload::NonASAP(NonASAPOp::TimeShift { shift, .. }) => Some(shift.offset_ms), _ => None, }) .unwrap_or(0) @@ -1478,11 +1482,12 @@ fn pane_roles(dag: &PhysicalASAPDAG) -> HashMap { for merge in dag .nodes .iter() - .filter(|n| matches!(n.payload, Payload::ASAP(ASAPOp::SummaryMerge {}))) + .filter(|n| matches!(n.payload, Payload::ASAP(ASAPOp::SummaryMerge { .. }))) { let panes: Vec<_> = producers(merge.id) .filter(|&id| { - materialized(id) && matches!(nodes[&id].payload, Payload::ASAP(ASAPOp::SummaryAgg { .. })) + materialized(id) + && matches!(nodes[&id].payload, Payload::ASAP(ASAPOp::SummaryAgg { .. })) }) .collect(); let Some(&newest) = panes.iter().min_by_key(|&&id| shift(id)) else { @@ -1570,11 +1575,7 @@ fn price_nodes( calibration: &Stage3Calibration, raw_bytes_per_sample: Option, ) -> Result { - let first = dag - .roots - .first() - .copied() - .unwrap_or(0); + let first = dag.roots.first().copied().unwrap_or(0); calibration.validate().map_err(|error| (first, error))?; let series = data .input_cardinality @@ -1639,220 +1640,221 @@ fn price_nodes( } }; let groups = |reduction: &Reduction| bounded_groups(reduction, input.rows); - let (out, estimate, detail) = match &node.payload { - Payload::NonASAP(operator) => match operator { - NonASAPOp::Scan { .. } => { - // At ingestion time, one second of arriving rows; at - // query time, as far back as the ranges reading it reach. - let span_ms = match ingestion { - true => 1_000, - false => { - scan_extent_ms(dag, &roles, node.id, 0).unwrap_or(DEFAULT_LOOKBACK_MS) - } - }; - let out = edge(scan_rows(rows_per_ms, span_ms)); - let estimate = estimate_operator( - PhysicalOperator::Scan, - OperatorStatistics::Scan { - edges: UnaryEdgeStatistics { - input: out, - output: out, - promql: None, + let (out, estimate, detail) = + match &node.payload { + Payload::NonASAP(operator) => match operator { + NonASAPOp::Scan { .. } => { + // At ingestion time, one second of arriving rows; at + // query time, as far back as the ranges reading it reach. + let span_ms = match ingestion { + true => 1_000, + false => scan_extent_ms(dag, &roles, node.id, 0) + .unwrap_or(DEFAULT_LOOKBACK_MS), + }; + let out = edge(scan_rows(rows_per_ms, span_ms)); + let estimate = estimate_operator( + PhysicalOperator::Scan, + OperatorStatistics::Scan { + edges: UnaryEdgeStatistics { + input: out, + output: out, + promql: None, + }, + source_read_bytes: out.bytes, }, - source_read_bytes: out.bytes, - }, - ); - (out, estimate, format!("scan {} samples", out.rows)) - } - NonASAPOp::Aggregate { - reduction, - measures, - .. - } => { + ); + (out, estimate, format!("scan {} samples", out.rows)) + } + NonASAPOp::Aggregate { + reduction, + measures, + .. + } => { + let group_count = groups(reduction); + let keys = match reduction { + Reduction::Reduce(keys) => keys.keys().len() as u64, + _ => 1, + }; + let out = edge(group_count); + let estimate = estimate_operator( + PhysicalOperator::HashAggregate { + grouping_key_count: keys, + accumulator_count: measures.len().max(1) as u64, + }, + OperatorStatistics::HashAggregate { + edges: unary(out), + group_count, + key_bytes: 16 * keys, + accumulator_bytes_per_group: 8, + }, + ); + ( + out, + estimate, + format!( + "hash aggregate {} rows into {group_count} groups", + input.rows + ), + ) + } + NonASAPOp::Sort { + keys, partition_by, .. + } => { + let partitions = if partition_by.keys().is_empty() { + 1 + } else { + DEFAULT_GROUP_COUNT.min(input.rows.max(1)) + }; + let estimate = estimate_operator( + PhysicalOperator::InMemoryComparisonSort { + ordering_key_count: keys.len() as u64, + partitioned: partitions > 1, + }, + OperatorStatistics::InMemoryComparisonSort { + edges: unary(input), + input_partitioning: split(input, partitions), + }, + ); + ( + input, + estimate, + format!("sort {} rows in {partitions} partitions", input.rows), + ) + } + NonASAPOp::Limit { + n, + offset, + partition_by, + .. + } => { + let partitions = partition_count(!partition_by.keys().is_empty()); + let limit = n.map_or(u64::MAX, |n| (n as u64).saturating_mul(partitions)); + let offset = (*offset as u64).saturating_mul(partitions); + let out = edge(selected_rows(input.rows.saturating_sub(offset), limit)); + let estimate = estimate_operator( + PhysicalOperator::Limit { limit, offset }, + OperatorStatistics::Limit { edges: unary(out) }, + ); + (out, estimate, format!("limit to {} rows", out.rows)) + } + // At query time a range keeps only its own span of a longer + // scan. Rows are ordered by time, so it seeks to that span + // and is charged for the rows it keeps, not those it skips + // (Q66). + NonASAPOp::TimeRange { range, .. } if !ingestion => { + let rows = (rows_per_ms * range.as_millis() as f64).round() as u64; + let out = edge(input.rows.min(rows.max(1))); + ( + out, + Ok(ResourceEstimate::new(out.rows as f64, width, 0)), + format!( + "time range {range:?}: keep {} of {} rows", + out.rows, input.rows + ), + ) + } + // A shift only re-labels time: the executor folds it into + // the time bounds of the read below it, so it does no + // per-row work (Q66). + NonASAPOp::TimeShift { shift, .. } => ( + input, + Ok(ResourceEstimate::new(0.0, 0, 0)), + format!( + "time shift {} ms: re-label {} rows, free", + shift.offset_ms, input.rows + ), + ), + other => { + let estimate = estimate_operator( + PhysicalOperator::PassThrough, + OperatorStatistics::PassThrough { + edges: unary(input), + }, + ); + let name = match other { + NonASAPOp::TimeRange { range, .. } => format!("time range {range:?}"), + _ => "operator".into(), + }; + (input, estimate, format!("{name}: pass {} rows", input.rows)) + } + }, + Payload::ASAP(ASAPOp::SummaryAgg { + family, reduction, .. + }) => { let group_count = groups(reduction); - let keys = match reduction { - Reduction::Reduce(keys) => keys.keys().len() as u64, - _ => 1, + let (depth, state_bytes) = summary_shape(family); + let out = EdgeStatistics { + rows: group_count, + bytes: group_count * state_bytes, }; - let out = edge(group_count); - let estimate = estimate_operator( - PhysicalOperator::HashAggregate { - grouping_key_count: keys, - accumulator_count: measures.len().max(1) as u64, - }, - OperatorStatistics::HashAggregate { - edges: unary(out), - group_count, - key_bytes: 16 * keys, - accumulator_bytes_per_group: 8, - }, - ); + let ops = input.rows as f64 * depth as f64; ( out, - estimate, + Ok(ResourceEstimate::new(ops, out.bytes, 0)), format!( - "hash aggregate {} rows into {group_count} groups", + "build {} into {group_count} states: {} rows x depth {depth}", + family_name(family), input.rows ), ) } - NonASAPOp::Sort { keys, partition_by } => { - let partitions = if partition_by.keys().is_empty() { - 1 - } else { - DEFAULT_GROUP_COUNT.min(input.rows.max(1)) + Payload::ASAP(ASAPOp::SummaryEstimate { query, .. }) => { + let rows = match query { + // The logical result, as an exact Sort → Limit sizes it. + // Items are at most the series: a whole-expression + // sketch reads several samples per ranked item. + SketchStatistic::TopK { k } => { + let (summarized, grouped) = summarized_rows(dag, &output, node.id); + selected_rows( + summarized.min(shape.series), + (*k as u64).saturating_mul(partition_count(grouped)), + ) + } + _ => input.rows, }; - let estimate = estimate_operator( - PhysicalOperator::InMemoryComparisonSort { - ordering_key_count: keys.len() as u64, - partitioned: partitions > 1, - }, - OperatorStatistics::InMemoryComparisonSort { - edges: unary(input), - input_partitioning: split(input, partitions), - }, - ); + let out = edge(rows); ( - input, - estimate, - format!("sort {} rows in {partitions} partitions", input.rows), + out, + Ok(ResourceEstimate::new(out.rows as f64, 0, 0)), + format!("estimate {} rows from {} states", out.rows, input.rows), ) } - NonASAPOp::Limit { - n, - offset, - partition_by, - } => { - let partitions = partition_count(!partition_by.keys().is_empty()); - let limit = n.map_or(u64::MAX, |n| (n as u64).saturating_mul(partitions)); - let offset = (*offset as u64).saturating_mul(partitions); - let out = edge(selected_rows(input.rows.saturating_sub(offset), limit)); - let estimate = estimate_operator( - PhysicalOperator::Limit { limit, offset }, - OperatorStatistics::Limit { edges: unary(out) }, + // One merge per input state. The output holds the union of the + // inputs' groups, bounded as one build over all their rows is, + // so readers see what they would see over the whole window. + Payload::ASAP(ASAPOp::SummaryMerge { .. }) => { + let merged: u64 = inputs.iter().map(|edge| edge.rows).sum(); + let reduction = dag.edges.iter().filter(|e| e.consumer == node.id).find_map( + |e| match &nodes[&e.producer].payload { + Payload::ASAP(ASAPOp::SummaryAgg { reduction, .. }) => Some(reduction), + _ => None, + }, ); - (out, estimate, format!("limit to {} rows", out.rows)) - } - // At query time a range keeps only its own span of a longer - // scan. Rows are ordered by time, so it seeks to that span - // and is charged for the rows it keeps, not those it skips - // (Q66). - NonASAPOp::TimeRange { range, .. } if !ingestion => { - let rows = (rows_per_ms * range.as_millis() as f64).round() as u64; - let out = edge(input.rows.min(rows.max(1))); + let rows = reduction.map_or(merged, |r| bounded_groups(r, merged)); + let out = EdgeStatistics { + rows, + bytes: rows * (input.bytes / input.rows.max(1)), + }; ( out, - Ok(ResourceEstimate::new(out.rows as f64, width, 0)), + Ok(ResourceEstimate::new(merged as f64, 0, 0)), format!( - "time range {range:?}: keep {} of {} rows", - out.rows, input.rows + "merge {merged} states from {} inputs into {rows}", + inputs.len() ), ) } - // A shift only re-labels time: the executor folds it into - // the time bounds of the read below it, so it does no - // per-row work (Q66). - NonASAPOp::TimeShift { shift } => ( - input, - Ok(ResourceEstimate::new(0.0, 0, 0)), - format!( - "time shift {} ms: re-label {} rows, free", - shift.offset_ms, input.rows - ), + Payload::ASAP(ASAPOp::FinalizeExactAccumulator { .. }) => ( + edge(input.rows), + Ok(ResourceEstimate::new(input.rows as f64, 0, 0)), + format!("finalize {} accumulators", input.rows), ), - other => { - let estimate = estimate_operator( - PhysicalOperator::PassThrough, - OperatorStatistics::PassThrough { - edges: unary(input), - }, - ); - let name = match other { - NonASAPOp::TimeRange { range, .. } => format!("time range {range:?}"), - _ => "operator".into(), - }; - (input, estimate, format!("{name}: pass {} rows", input.rows)) - } - }, - Payload::ASAP(ASAPOp::SummaryAgg { - family, reduction, .. - }) => { - let group_count = groups(reduction); - let (depth, state_bytes) = summary_shape(family); - let out = EdgeStatistics { - rows: group_count, - bytes: group_count * state_bytes, - }; - let ops = input.rows as f64 * depth as f64; - ( - out, - Ok(ResourceEstimate::new(ops, out.bytes, 0)), - format!( - "build {} into {group_count} states: {} rows x depth {depth}", - family_name(family), - input.rows - ), - ) - } - Payload::ASAP(ASAPOp::SummaryEstimate { query }) => { - let rows = match query { - // The logical result, as an exact Sort → Limit sizes it. - // Items are at most the series: a whole-expression - // sketch reads several samples per ranked item. - SketchStatistic::TopK { k } => { - let (summarized, grouped) = summarized_rows(dag, &output, node.id); - selected_rows( - summarized.min(shape.series), - (*k as u64).saturating_mul(partition_count(grouped)), - ) - } - _ => input.rows, - }; - let out = edge(rows); - ( - out, - Ok(ResourceEstimate::new(out.rows as f64, 0, 0)), - format!("estimate {} rows from {} states", out.rows, input.rows), - ) - } - // One merge per input state. The output holds the union of the - // inputs' groups, bounded as one build over all their rows is, - // so readers see what they would see over the whole window. - Payload::ASAP(ASAPOp::SummaryMerge {}) => { - let merged: u64 = inputs.iter().map(|edge| edge.rows).sum(); - let reduction = dag - .edges - .iter() - .filter(|e| e.consumer == node.id) - .find_map(|e| match &nodes[&e.producer].payload { - Payload::ASAP(ASAPOp::SummaryAgg { reduction, .. }) => Some(reduction), - _ => None, - }); - let rows = reduction.map_or(merged, |r| bounded_groups(r, merged)); - let out = EdgeStatistics { - rows, - bytes: rows * (input.bytes / input.rows.max(1)), - }; - ( - out, - Ok(ResourceEstimate::new(merged as f64, 0, 0)), - format!( - "merge {merged} states from {} inputs into {rows}", - inputs.len() - ), - ) - } - Payload::ASAP(ASAPOp::FinalizeExactAccumulator {}) => ( - edge(input.rows), - Ok(ResourceEstimate::new(input.rows as f64, 0, 0)), - format!("finalize {} accumulators", input.rows), - ), - _ => ( - edge(input.rows), - Ok(ResourceEstimate::new(input.rows as f64, 0, 0)), - format!("{} rows", input.rows), - ), - }; + _ => ( + edge(input.rows), + Ok(ResourceEstimate::new(input.rows as f64, 0, 0)), + format!("{} rows", input.rows), + ), + }; let cost = estimate .and_then(|estimate| estimate.calibrated_cost(&resources)) .map_err(|error| (node.id, error))?; @@ -1985,10 +1987,7 @@ fn raw_retention( .iter() .filter(|n| { n.output_state.timing.is_query_time() - && matches!( - n.payload, - Payload::NonASAP(NonASAPOp::Scan { .. }) - ) + && matches!(n.payload, Payload::NonASAP(NonASAPOp::Scan { .. })) }) .map(|n| { let span_ms = scan_extent_ms(dag, roles, n.id, 0).unwrap_or(DEFAULT_LOOKBACK_MS); @@ -2755,10 +2754,7 @@ mod tests { } fn is_scan(payload: &Payload) -> bool { - matches!( - payload, - Payload::NonASAP(NonASAPOp::Scan { .. }) - ) + matches!(payload, Payload::NonASAP(NonASAPOp::Scan { .. })) } fn is_build(payload: &Payload) -> bool { @@ -3241,14 +3237,12 @@ mod tests { let (_, mut stage2) = realize(variant, &raw, &demand, &data, &models).unwrap(); let candidate = stage2.candidates.swap_remove(0); let cost = assess(&candidate, &demand, &data, &models).unwrap(); - let of = |pick: fn(&NonASAPOpKind) -> bool| -> Vec<&NodeCost> { + let of = |pick: fn(&NonASAPOp) -> bool| -> Vec<&NodeCost> { candidate .dag .nodes .iter() - .filter( - |n| matches!(&n.payload, Payload::NonASAP(operator) if pick(operator)), - ) + .filter(|n| matches!(&n.payload, Payload::NonASAP(operator) if pick(operator))) .map(|n| &cost.per_node[&n.id]) .collect() }; @@ -3389,7 +3383,8 @@ mod tests { let (p, _) = maintained_panes(&run); let p = p.clone(); // data(): 10 000 series; the newest pane keeps itself and 5 others. - let Payload::ASAP(ASAPOp::SummaryAgg { family, .. }) = &node_of(&p.dag, is_build).payload else { + let Payload::ASAP(ASAPOp::SummaryAgg { family, .. }) = &node_of(&p.dag, is_build).payload + else { unreachable!() }; let retained = 6 * 10_000 * summary_shape(family).1;