Expose accumulator state to allow prefix scanning - #24035
Conversation
ae37c0b to
65014d5
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #24035 +/- ##
==========================================
+ Coverage 80.91% 81.05% +0.13%
==========================================
Files 1103 1106 +3
Lines 377219 382677 +5458
Branches 377219 382677 +5458
==========================================
+ Hits 305244 310182 +4938
- Misses 53775 54142 +367
- Partials 18200 18353 +153 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Thanks for this contribution @avantgardnerio ! I'd find it helpful if you'd elaborate a little bit more about the motivation for this change in the PR description. For example, some intended use-cases, what kind of performance improvement this unlocks, etc. |
|
@neilconway I'm trying to speed up window functions using parallel prefix scans. I am presently incubating this in Ballista, and this is the minimum API exposure that I need to do it for non-decomposable operations like The jury is still out about re-partition cost vs performance benefit, but the signs are hopeful:
And at least from a big-O time perspective (table 1) it should be optimal for some queries ( |
docs � Conflicts: � datafusion/physical-plan/src/windows/bounded_window_agg_exec.rs
0767f73 to
13023b2
Compare
|
@gene-bordegaray and @JSOD11 you guys might be interested as well. |
alamb
left a comment
There was a problem hiding this comment.
Thanks @avantgardnerio and @neilconway -- I left some comments
alamb
left a comment
There was a problem hiding this comment.
I may not fully understand prefix scanning, but it seems to me like this API will only give you access to the window state for the single last row in each partition.
Don't you potentially need access to the window state for the last N rows in a partition (e.g the HALO rows) 🤔
|
FWIW claude claims this doesn't get run with windows like UNBOUNDED PRECEDING → CURRENT ROW |
|
I think it would also be super useful to add some sort of example / test that shows how you intend to use this API (for exmple some simple example for computing a window function in parallel or something 🤔 that way we could see the API in action |
Thanks @alamb ! That was a critical bug that would have defeated the whole point. It is now fixed and asserted in |
Yes, this is exactly what is required.
No, not for prefix scanning. (answer below)
The HPC "halo" term is a good fit for bounded preceding/following (surrounding cells, in 1D) but doesn't extend cleanly to "last row of every other partition." Regardless of the name, this PR doesn't take that approach - because although it works for SUM, and decomposes for AVG (sum+count), it fails by the time you get to arbitrary accumulators like approx_distinct.
Which is exactly where (the newly added) Edit: added the qualifier (DF) partition to distinguish between the ambiguously named (SQL window) partition. |
|
Pseudo code, stripped directly from the |
Trait is `pub` but its containing module `bounded_window_agg_exec` is private, so there was no public path to it — Ballista couldn't name it, and rustdoc rejected the intra-doc link on `with_state_observer` as pointing to a private item (breaking `cargo doc -D warnings`). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Match the field's type so with_new_children collapses to a single chained call and the setter can also clear a previously-installed observer. Addresses apache#24035 review comment 3738803158. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The method now always mutates when called and takes the observer as a required argument; the "is observer installed?" check moves to the caller in `compute_aggregates`. Removes the "&mut self that only mutates when observer is set" shape. Addresses apache#24035 review comment 3738822701. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rename the trait method to `finalize_window_aggregate` and split its signature so the callback fires once per aggregate window expression per closing PARTITION BY group, receiving that expression's Arc and its own `Accumulator::state` directly. Non-aggregate window functions no longer fire the callback at all. Removes the per-partition-key `Vec<Option<Vec<ScalarValue>>>` wrapper allocation, and gives the observer the window-expression context needed to disambiguate calls when the exec carries multiple window expressions. Leaves room to add a peer `finalize_window_function` later for built-in (non-aggregate) window functions. Addresses apache#24035 review comments 3738790091 and 3738816488. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…close tests The two `test_finalized_state_observer_*` tests were structurally identical apart from the window frame. Fold their common setup and assertions into a single async helper that takes the frame, so each test body is now just the frame construction + a one-line comment explaining which causality regime it exercises. Addresses apache#24035 review comment 3738845164. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ox_distinct) Adds `test_prefix_merge_across_tasks_approx_distinct` which runs two BWAG(approx_distinct(sn)) tasks over disjoint-but-overlapping slices, takes each task's observed EOS state, feeds both into a fresh accumulator via `Accumulator::merge_batch`, and asserts the resulting distinct count matches a single-BWAG oracle over the concatenated input. This is the load-bearing contract for the parallel-window use case that motivated exposing accumulator state: non-decomposable aggregates like approx_distinct must survive round-tripping through the observer and merge_batch to be usable in a prefix-merge pipeline. The count/sum tests already in this file exercise decomposable aggregates only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Hi @alamb , I appreciate your feedback, and I think I addressed all of it. I also threw in a defensive test for merging HLLs just to be sure. If there's anything I missed, please let me know and I'll address that too. I really appreciate you sticking with this through multiple rounds of reviews 🙂 |

Summary
Expose state of aggregate streams within BWAG so downstream prefix scanning can take place.
API