Describe the bug
When SortExec runs in TopK mode (ORDER BY ... LIMIT k on unsorted input) and the k result rows span more than one output batch, its output_batches metric reports 1 regardless of how many batches the operator actually emits.
For example, a top-25 query at datafusion.execution.batch_size = 10 emits three batches ([10, 10, 5] rows) but reports output_batches=1.
To Reproduce
The following standalone test (e.g. dropped into datafusion/core/tests/) fails on main:
use std::sync::Arc;use datafusion::common::Result;use datafusion::physical_plan::metrics::MetricValue;use datafusion::physical_plan::sorts::sort::SortExec;use datafusion::physical_plan::{ExecutionPlan, collect};use datafusion::prelude::*;/// The `output_batches` metric of a TopK sort should equal the number of/// batches the operator emits to its consumer.#[tokio::test]asyncfntopk_output_batches_metric_matches_emitted_batches() -> Result<()>{// A top-25 over 100 unsorted rows, at batch_size 10, so the TopK result// must be emitted as multiple batches.let config = SessionConfig::new().with_batch_size(10).with_target_partitions(1);let ctx = SessionContext::new_with_config(config);
ctx.sql("CREATE TABLE t AS SELECT value FROM range(0, 100)").await?
.collect().await?;let df = ctx
.sql("SELECT value FROM t ORDER BY value DESC LIMIT 25").await?;let plan = df.create_physical_plan().await?;let batches = collect(Arc::clone(&plan), ctx.task_ctx()).await?;let emitted_sizes:Vec<usize> = batches.iter().map(|b| b.num_rows()).collect();let sort = find_sort(&plan).expect("plan should contain SortExec");let metrics = sort.metrics().expect("SortExec should have metrics");let output_batches = metrics
.sum(|m| matches!(m.value(),MetricValue::OutputBatches(_))).expect("output_batches metric should be present").as_usize();// The 25 result rows arrive as three batches of at most batch_size rowsassert_eq!(emitted_sizes, vec![10,10,5]);// ... so the metric should report three output batchesassert_eq!(
output_batches,
emitted_sizes.len(),"output_batches metric disagrees with the number of emitted batches");Ok(())}fnfind_sort(plan:&Arc<dynExecutionPlan>) -> Option<Arc<dynExecutionPlan>>{if plan.downcast_ref::<SortExec>().is_some(){returnSome(Arc::clone(plan));}
plan.children().into_iter().find_map(find_sort)}Output:
assertion `left == right` failed: output_batches metric disagrees with the number of emitted batches
left: 1
right: 3
Expected behavior
output_batches matches the number of batches the operator emits to its consumer (3 in the reproducer above).
Additional context
Only the TopK path is affected. SortExec without a fetch, and SortExec with a fetch over already-sorted input (the LimitStream path), report output_batches correctly.
Describe the bug
When
SortExecruns in TopK mode (ORDER BY ... LIMIT kon unsorted input) and the k result rows span more than one output batch, itsoutput_batchesmetric reports1regardless of how many batches the operator actually emits.For example, a top-25 query at
datafusion.execution.batch_size = 10emits three batches ([10, 10, 5]rows) but reportsoutput_batches=1.To Reproduce
The following standalone test (e.g. dropped into
datafusion/core/tests/) fails onmain:Output:
Expected behavior
output_batchesmatches the number of batches the operator emits to its consumer (3in the reproducer above).Additional context
Only the TopK path is affected.
SortExecwithout afetch, andSortExecwith afetchover already-sorted input (theLimitStreampath), reportoutput_batchescorrectly.