Skip to content

chore(deps): bump datafusion to 55 and arrow to 59 - #37

Merged
zfarrell merged 6 commits into
mainfrom
chore/datafusion-55
Aug 28, 2026
Merged

zfarrell merged 6 commits into
mainfrom
chore/datafusion-55

Conversation

@zfarrell

Copy link
Copy Markdown

Moves the workspace to DataFusion 55 and the arrow/parquet family to 59.2, adapting to the required API changes (FileSource/ExecutionPlan::apply_expressions, ConfigNonZeroUsize batch size, PruningPredicateBuilder, StatisticsContext, replace_children_if_necessary, VariantArray::typed_value_column).

DataFusion 55 also lowers the repartition_file_min_size default from 10 MiB to 1 MiB, which splits the 2.3 MB test file across scan partitions and made the cache-trace snapshots depend on scheduling and core count; the affected tests now pin the threshold above the file size.

Comment on lines +153 to +155
// `FilePruner::try_new` itself decides whether a pruner is worth
// building: it returns `None` for a purely static predicate over a
// file with no usable column statistics.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit: the pre-existing sentence a few lines above ("It is assumed that there is no point in doing pruning here if the predicate is not dynamic, as it would have been done at planning time") described exactly the is_dynamic_physical_expr(p) || partitioned_file.has_statistics() guard this commit removes, so it now contradicts this new comment. Worth dropping those two lines so the block reads consistently. (not blocking)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — those two lines described the is_dynamic_physical_expr(p) || partitioned_file.has_statistics() guard this PR removed, so they now contradict the comment below them. Dropped them in 7563c2e.

/// scan stays single-partition and the snapshots stay reproducible.
pub(super) fn cache_test_config() -> SessionConfig {
let mut config = SessionConfig::new();
config.options_mut().optimizer.repartition_file_min_size = 16 * 1024 * 1024;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: pinning the threshold makes the snapshots reproducible, but it also means no test now exercises the multi-partition scan path against the shared cache — target_partitions = 4 in create_session_context_with_liquid_cache no longer produces concurrent scan partitions for this file. That concurrency was previously covered incidentally, and a real deployment on a >1 MiB file will hit it by default. Consider adding one test that leaves repartition_file_min_size at the DF 55 default and asserts only order-independent properties (query results, has_cache_hits()), so the concurrent-admission path stays covered without the flakiness. (not blocking)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — pinning the threshold did drop coverage of the concurrent multi-partition scan, which is now the default path for any file over 1 MiB. Added test_multi_partition_scan_shares_cache in a7ac25a: it leaves repartition_file_min_size at the DF 55 default, asserts the leaf scan really is multi-partition, and checks only order-independent properties (rows compared as a sorted multiset against a single-partition run, plus has_cache_hits()). The snapshot tests keep their pin.

Comment on lines +326 to +328
let stats = StatisticsContext::new()
.compute(plan.as_ref(), &StatisticsArgs::new())
.unwrap();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit: the old call was partition_statistics(None), i.e. explicitly "across all partitions". Worth double-checking that StatisticsArgs::new() defaults to the same thing rather than partition 0 — the admin endpoint would silently start reporting a fraction of the row/byte counts if it doesn't. If it does default to all partitions, ignore this. (not blocking)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked the DF 55 source: StatisticsArgs::new() is all-partitions, so this is equivalent to the old partition_statistics(None). In datafusion-physical-plan-55.0.0/src/statistics.rs, StatisticsArgs is #[derive(Debug, Default, Clone)] pub struct StatisticsArgs { partition: Option<usize> } (L73-76) and pub fn new() -> Self { Default::default() } (L83-85), i.e. partition: None; its doc comment states "By default the partition is set to None (statistics should be computed for the entire plan)" and set_partition documents None as "statistics should be computed for the entire plan". StatisticsContext::compute only restricts to a partition when args.partition() is Some(idx) (L159-168). Upstream itself uses this exact call as the partition_statistics(None) equivalent — see operator_statistics/mod.rs L358-362, where StatisticsContext::new().compute(plan, &StatisticsArgs::new()) is described as partition_statistics(None) with no overhead. No change needed.

claude[bot]
claude Bot previously approved these changes Aug 28, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The full diff was omitted from the review context, so I fetched it with gh pr diff and read the post-merge files for src/datafusion/src/reader/plantime/source.rs, opener.rs, src/datafusion/src/optimizers/mod.rs, src/datafusion-client/src/client_exec.rs, optimizer.rs, src/datafusion-local/src/lib.rs, src/datafusion-local/src/tests/mod.rs, and src/datafusion-server/src/admin_server/handlers.rs.

The API adaptations look like faithful ports: replace_children_if_necessary matches the hand-rolled ptr-eq logic it replaces, PruningPredicateBuilder is passed the same schema the old PruningPredicate::try_new got (and PrunableStatistics still uses the same one), and LiquidCacheClientExec::apply_expressions is correct given the node holds no expressions of its own and exposes remote_plan as a child. LiquidParquetSource::apply_expressions covers both predicate and projection, which are the only expression-bearing fields on the struct. The TPC-H snapshot churn is DF 55 display changes (decimal rendering, join_type line) plus planner sort/projection reordering — no structural change at the LiquidCacheClientExec pushdown boundary.

The cache_test_config workaround is well reasoned and applied to every test that reads nano_hits.parquet and asserts order-sensitive cache state; src/datafusion-server/src/tests/mod.rs also uses that file but only asserts query results, so it doesn't need it.

CI checks were all still queued or in progress when this review started, so I have no test results to report either way. Three non-blocking comments inline.

@codecov

codecov Bot commented Aug 28, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

📊 Benchmark Comparison

Current: 12aeb14d (Liquid) vs Baseline: 12aeb14d (DataFusionDefault)

Query Cold Time Δ Warm Time Δ CPU Time Δ
Q1 2.0ms (2.0ms) +0.0% 0.000ms (0.000ms) +0.0% 0.000ms (0.000ms) +0.0%
Q2 11.0ms (6.0ms) +83.3% 3.0ms (4.5ms) -33.3% 6.0ms (6.2ms) -4.0%
Q3 16.0ms (12.0ms) +33.3% 5.8ms (11.2ms) -48.9% 2.0ms (22.8ms) -91.2%
Q4 18.0ms (13.0ms) +38.5% 3.8ms (12.0ms) -68.8% 1.8ms (24.5ms) -92.9%
Q5 57.0ms (54.0ms) +5.6% 43.8ms (52.5ms) -16.7% 3.2ms (25.5ms) -87.3%
Q6 150.0ms (110.0ms) +36.4% 72.2ms (106.0ms) -31.8% 23.5ms (79.8ms) -70.5%
Q7 1.0ms (1.0ms) +0.0% 0.000ms (0.500ms) -100.0% 0.000ms (0.000ms) +0.0%
Q8 8.0ms (6.0ms) +33.3% 4.8ms (6.2ms) -24.0% 6.8ms (7.2ms) -6.9%
Q9 91.0ms (86.0ms) +5.8% 73.8ms (86.5ms) -14.7% 4.0ms (42.8ms) -90.6%
Q10 101.0ms (95.0ms) +6.3% 78.5ms (92.0ms) -14.7% 5.8ms (62.2ms) -90.8%
Q11 45.0ms (25.0ms) +80.0% 22.2ms (26.5ms) -16.0% 38.8ms (39.0ms) -0.6%
Q12 55.0ms (29.0ms) +89.7% 24.5ms (30.0ms) -18.3% 40.8ms (44.2ms) -7.9%
Q13 197.0ms (118.0ms) +66.9% 120.2ms (114.2ms) +5.3% 65.0ms (81.0ms) -19.8%
Q14 387.0ms (143.0ms) +170.6% 156.5ms (152.5ms) +2.6% 111.5ms (115.5ms) -3.5%
Q15 246.0ms (105.0ms) +134.3% 123.0ms (108.2ms) +13.6% 78.0ms (97.2ms) -19.8%
Q16 141.0ms (117.0ms) +20.5% 137.2ms (111.8ms) +22.8% 4.8ms (25.0ms) -81.0%
Q17 548.0ms (239.0ms) +129.3% 295.2ms (233.5ms) +26.4% 111.2ms (107.5ms) +3.5%
Q18 465.0ms (233.0ms) +99.6% 295.5ms (233.8ms) +26.4% 111.0ms (107.2ms) +3.5%
Q19 881.0ms (431.0ms) +104.4% 440.5ms (460.5ms) -4.3% 104.2ms (156.8ms) -33.5%
Q20 15.0ms (14.0ms) +7.1% 3.2ms (12.0ms) -72.9% 6.2ms (24.2ms) -74.2%
Q21 749.0ms (173.0ms) +332.9% 286.5ms (174.8ms) +63.9% 505.2ms (278.2ms) +81.6%
Q22 1.09s (174.0ms) +525.3% 325.0ms (168.0ms) +93.5% 175.5ms (347.2ms) -49.5%
Q23 2.30s (462.0ms) +398.1% 998.8ms (474.8ms) +110.4% 517.5ms (749.5ms) -31.0%
Q24 17.58s (927.0ms) +1796.1% 798.8ms (964.8ms) -17.2% 632.5ms (2.67s) -76.3%
Q25 172.0ms (72.0ms) +138.9% 14.2ms (59.2ms) -75.9% 36.8ms (121.5ms) -69.8%
Q26 95.0ms (46.0ms) +106.5% 19.5ms (46.2ms) -57.8% 49.2ms (85.0ms) -42.1%
Q27 177.0ms (62.0ms) +185.5% 26.2ms (64.5ms) -59.3% 75.8ms (123.0ms) -38.4%
Q28 964.0ms (220.0ms) +338.2% 297.5ms (220.8ms) +34.8% 359.2ms (283.2ms) +26.8%
Q29 1.74s (984.0ms) +76.8% 1.10s (1.01s) +8.8% 583.5ms (344.0ms) +69.6%
Q30 33.0ms (26.0ms) +26.9% 22.2ms (28.0ms) -20.5% 6.2ms (22.0ms) -71.6%
Q31 282.0ms (109.0ms) +158.7% 74.2ms (105.2ms) -29.5% 44.0ms (143.8ms) -69.4%
Q32 558.0ms (114.0ms) +389.5% 98.8ms (106.8ms) -7.5% 55.8ms (152.5ms) -63.4%
Q33 385.0ms (366.0ms) +5.2% 338.0ms (360.0ms) -6.1% 8.5ms (74.5ms) -88.6%
Q34 1.18s (443.0ms) +165.5% 518.2ms (454.5ms) +14.0% 339.8ms (275.8ms) +23.2%
Q35 1.08s (441.0ms) +146.0% 515.8ms (449.8ms) +14.7% 356.0ms (284.5ms) +25.1%
Q36 111.0ms (102.0ms) +8.8% 100.5ms (109.8ms) -8.4% 4.0ms (25.0ms) -84.0%
Q37 335.0ms (111.0ms) +201.8% 90.8ms (99.8ms) -9.0% 46.0ms (70.2ms) -34.5%
Q38 78.0ms (46.0ms) +69.6% 29.2ms (47.5ms) -38.4% 17.2ms (27.2ms) -36.7%
Q39 280.0ms (48.0ms) +483.3% 12.5ms (49.0ms) -74.5% 11.0ms (73.2ms) -85.0%
Q40 705.0ms (192.0ms) +267.2% 228.0ms (187.5ms) +21.6% 85.5ms (126.5ms) -32.4%
Q41 25.0ms (23.0ms) +8.7% 12.5ms (18.5ms) -32.4% 7.2ms (16.5ms) -56.1%
Q42 21.0ms (17.0ms) +23.5% 10.8ms (18.5ms) -41.9% 7.8ms (16.2ms) -52.3%
Q43 18.0ms (18.0ms) +0.0% 11.0ms (16.5ms) -33.3% 7.0ms (11.5ms) -39.1%

⚠️ LiquidCache is slower on 14 queries (warm)

  • Q23: warm +110.4% (998.8ms vs 474.8ms)
  • Q22: warm +93.5% (325.0ms vs 168.0ms)
  • Q21: warm +63.9% (286.5ms vs 174.8ms)
  • Q28: warm +34.8% (297.5ms vs 220.8ms)
  • Q17: warm +26.4% (295.2ms vs 233.5ms)
  • Q18: warm +26.4% (295.5ms vs 233.8ms)
  • Q16: warm +22.8% (137.2ms vs 111.8ms)
  • Q40: warm +21.6% (228.0ms vs 187.5ms)
  • Q35: warm +14.7% (515.8ms vs 449.8ms)
  • Q34: warm +14.0% (518.2ms vs 454.5ms)
  • Q15: warm +13.6% (123.0ms vs 108.2ms)
  • Q29: warm +8.8% (1.10s vs 1.01s)
  • Q13: warm +5.3% (120.2ms vs 114.2ms)
  • Q14: warm +2.6% (156.5ms vs 152.5ms)

Compared Liquid vs DataFusionDefault on the same runner
Cold Time: first iteration; Warm Time: average of remaining iterations.

claude[bot]
claude Bot previously approved these changes Aug 28, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cycle 2. The ## Full diff block was omitted as too large, so I fetched the patch with gh pr diff 37 and read the post-change files directly: src/datafusion/src/reader/plantime/source.rs, src/datafusion/src/reader/plantime/opener.rs, src/datafusion-client/src/client_exec.rs, src/datafusion-local/src/tests/mod.rs, src/datafusion-local/src/tests/{squeeze,filter_limit,page_index}.rs, src/datafusion-server/src/admin_server/handlers.rs, and the two datafusion-local snapshots.

The since-diff (provide_schema2.snap) moves SortExec below the ProjectionExec onto the raw date_bin group key. That is a 1:1 projection above the sort, so the SortPreservingMergeExec on zo_sql_key still sees the same order — plan-shape refresh only.

Spot-checked the rest of the port: LiquidParquetSource::apply_expressions covers both expression-holding fields (predicate, projection); LiquidCacheClientExec genuinely holds none of its own; replace_children_if_necessary reproduces the old ptr-eq short circuit; the PruningPredicateBuilder conversions keep the same schema. cache_test_config() is applied to every test that reads the 2.3 MB nano_hits.parquet — the other SessionConfig::new() tests write their own small files, so they stay single-partition regardless.

No blocking issues. The three prior threads were all non-blocking and I am not re-raising them. CI checks were still queued or in progress at review time, so I am not asserting anything about test results.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The full diff was omitted from my review context, so I fetched it with gh pr diff 37 and read the non-lockfile portion in full, plus the surrounding code in src/datafusion-local/src/tests/mod.rs, src/core/src/cache/core.rs, src/core/src/cache/observer/mod.rs, src/datafusion-client/src/client_exec.rs, src/datafusion/src/optimizers/mod.rs, and src/datafusion/src/reader/plantime/source.rs from the checkout.

Both prior nits are addressed: the stale pruning comment is gone in 7563c2e, and test_multi_partition_scan_shares_cache in a7ac25a covers the default repartition_file_min_size path with order-independent assertions only. The cache.storage().stats() counter-reset idiom it uses is correct — LiquidCache::stats goes through Observer::runtime_snapshot, which consumes and zeroes the runtime counters.

No blocking issues. Note that CI was still queued/in progress when this review ran, so I have not seen results for the new test or the refreshed snapshots.

@zfarrell
zfarrell merged commit ce81773 into main Aug 28, 2026
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant