Skip to content

Commit 1faa09f

Browse files
committed
add more comments
1 parent db2ac80 commit 1faa09f

2 files changed

Lines changed: 258 additions & 40 deletions

File tree

  • datafusion

datafusion/core/tests/physical_optimizer/filter_pushdown/mod.rs

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,9 +1214,67 @@ async fn test_hashjoin_dynamic_filter_pushdown_partitioned() {
12141214
);
12151215
}
12161216

1217-
/// Test that simulates behavior when some build side partitions are much slower to finish than others.
1218-
/// In these cases we push down _partial_ filters that only apply scan filters to rows the belong to build side partitions that have completed.
1219-
/// Once all build side partitions complete the full filter is pushed down without any hash key partition calculations.
1217+
/// Test demonstrating progressive dynamic filter evolution in partitioned hash joins
1218+
///
1219+
/// This test validates that instead of waiting for all
1220+
/// build-side partitions to complete before applying any filters, we apply partial filters
1221+
/// immediately as each partition completes.
1222+
/// To be able to evaluate partial filters we need to know which partition each probe row belongs to,
1223+
/// so we push down a hash function to the probe side that computes the same hash as repartitioning will later on.
1224+
///
1225+
/// ## Test Scenario Setup
1226+
///
1227+
/// - **Build side**: Values [1, 2, 3, 4] distributed across 3 hash partitions
1228+
/// - **Probe side**: Values [2, 3] that need to be filtered
1229+
/// - **Partition 1 is artificially slowed**: Simulates real-world partition skew
1230+
///
1231+
/// ## Progressive Filter Evolution Demonstration
1232+
///
1233+
/// The test shows how the dynamic filter evolves through three distinct phases:
1234+
///
1235+
/// ### Phase 1: Initial State (All Partitions Building)
1236+
/// ```sql
1237+
/// -- No filter applied yet
1238+
/// predicate=DynamicFilterPhysicalExpr [ true ]
1239+
/// ```
1240+
/// → All probe-side data passes through unfiltered
1241+
///
1242+
/// ### Phase 2: Progressive Filtering (Some Partitions Complete)
1243+
/// ```sql
1244+
/// -- Hash-based progressive filter after partition 0 completes
1245+
/// predicate=DynamicFilterPhysicalExpr [
1246+
/// CASE repartition_hash(id@0) % 3
1247+
/// WHEN 0 THEN id@0 >= 3 AND id@0 <= 3 -- Only partition 0 bounds known
1248+
/// ELSE true -- Pass through partitions 1,2 data
1249+
/// END
1250+
/// ]
1251+
/// ```
1252+
/// → Filters probe data for partition 0, passes through everything else safely
1253+
///
1254+
/// ### Phase 3: Final Optimization (All Partitions Complete)
1255+
/// ```sql
1256+
/// -- Optimized bounds-only filter
1257+
/// predicate=DynamicFilterPhysicalExpr [
1258+
/// id@0 >= 3 AND id@0 <= 3 OR -- Partition 0 bounds
1259+
/// id@0 >= 2 AND id@0 <= 2 OR -- Partition 1 bounds
1260+
/// id@0 >= 1 AND id@0 <= 4 -- Partition 2 bounds
1261+
/// ]
1262+
/// ```
1263+
/// → Bounds filter with no hash computation overhead
1264+
///
1265+
/// ## Correctness Validation
1266+
///
1267+
/// The test verifies:
1268+
/// 1. **No False Negatives**: All valid join results [2,3] are preserved throughout
1269+
/// 2. **Progressive Improvement**: Filter selectivity increases as partitions complete
1270+
/// 3. **Final Optimization**: Hash-based expressions are removed when all partitions finish
1271+
/// 4. **Partition Isolation**: Each partition's filter only affects its own hash bucket
1272+
///
1273+
/// ## Real-World Impact
1274+
///
1275+
/// This optimization addresses common production scenarios where:
1276+
/// - Some partitions finish much faster than others (data skew)
1277+
/// - Waiting for large build sides before starting the probe sides increases latency
12201278
#[tokio::test]
12211279
#[cfg(not(feature = "force_hash_collisions"))] // this test relies on hash partitioning to separate rows
12221280
async fn test_hashjoin_progressive_filter_reporting() {
@@ -1295,7 +1353,8 @@ async fn test_hashjoin_progressive_filter_reporting() {
12951353
.unwrap(),
12961354
) as Arc<dyn ExecutionPlan>;
12971355

1298-
// expect the predicate to be pushed down into the probe side DataSource
1356+
// Verify the initial optimization - should show DynamicFilterPhysicalExpr is set up
1357+
// but not yet populated with any bounds (shows as "true" initially)
12991358
insta::assert_snapshot!(
13001359
OptimizationTest::new(Arc::clone(&plan), FilterPushdown::new_post_optimization(), true),
13011360
@r"
@@ -1347,7 +1406,13 @@ async fn test_hashjoin_progressive_filter_reporting() {
13471406
}
13481407
}
13491408

1350-
// Now check what our filter looks like
1409+
// CRITICAL VALIDATION: This snapshot shows the progressive filter in action!
1410+
// After partition 0 completes (but partition 1 is still blocked), we see:
1411+
// - CASE repartition_hash(id@0) % 3 WHEN 0 THEN id@0 >= 3 AND id@0 <= 3 ELSE true END
1412+
// This means:
1413+
// - For rows that hash to partition 0: Apply bounds check (id >= 3 AND id <= 3)
1414+
// - For rows that hash to partitions 1,2: Pass everything through (ELSE true)
1415+
// This is the core of progressive filtering - partial filtering without false negatives!
13511416
#[cfg(not(feature = "force_hash_collisions"))]
13521417
insta::assert_snapshot!(
13531418
format!("{}", format_plan_for_test(&plan)),
@@ -1385,7 +1450,11 @@ async fn test_hashjoin_progressive_filter_reporting() {
13851450
batches.push(batch.unwrap());
13861451
}
13871452

1388-
// Look at the final plan
1453+
// FINAL OPTIMIZATION VALIDATION: All partitions complete - filter is now optimized!
1454+
// The hash-based CASE expression has been replaced with a simple OR of bounds:
1455+
// - id@0 >= 3 AND id@0 <= 3 OR id@0 >= 2 AND id@0 <= 2 OR id@0 >= 1 AND id@0 <= 4
1456+
// This is much more efficient - no hash computation needed, just bounds checks.
1457+
// Each OR clause represents one partition's bounds: [3,3], [2,2], [1,4]
13891458
insta::assert_snapshot!(
13901459
format!("{}", format_plan_for_test(&plan)),
13911460
@r"

0 commit comments

Comments
 (0)