Repository navigation
fix: reset aggregate dynamic filters between recursive iterations #26056
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0de6c24
1e1b704
8a56599
39553f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,6 +29,7 @@ use datafusion_common::{ | |
| tree_node::{Transformed, TransformedResult, TreeNode}, | ||
| }; | ||
|
|
||
| use crate::expressions::lit; | ||
| use datafusion_expr::ColumnarValue; | ||
| use datafusion_expr_common::dyn_eq::DynHash; | ||
|
|
||
|
|
@@ -343,6 +344,35 @@ impl DynamicFilterPhysicalExpr { | |
| Ok(()) | ||
| } | ||
|
|
||
| /// Reset this dynamic filter so its producer can publish a new expression | ||
| /// during a later execution of the same plan. | ||
| pub fn reset(&self) -> Result<()> { | ||
| let new_expr = Self::remap_children( | ||
| &self.children, | ||
| self.remapped_children.as_ref(), | ||
| lit(true), | ||
| )?; | ||
|
|
||
| let mut current = self.inner.write(); | ||
| // Keep the generation increasing: `current()` caches by generation, so going | ||
| // back would let a later update hit the previous run's cached expression. | ||
| let new_generation = current.generation + 1; | ||
| *current = Inner { | ||
| expression_id: current.expression_id, | ||
| generation: new_generation, | ||
| expr: new_expr, | ||
| is_complete: false, | ||
| }; | ||
| drop(current); | ||
|
|
||
| // The channel still holds `Complete` from the previous run; publish | ||
| // `InProgress` so `wait_complete()` waits for this run's filter. | ||
| let _ = self.state_watch.send(FilterState::InProgress { | ||
| generation: new_generation, | ||
| }); | ||
|
Comment on lines
+370
to
+372
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, why send this message and why this generation vs. e.g.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The watch channel still holds @mikamikasuki and a comment here like: // The channel still holds `Complete` from the previous run; publish
// `InProgress` so `wait_complete()` waits for this run's filter. |
||
| Ok(()) | ||
| } | ||
|
|
||
| /// Mark this dynamic filter as complete and broadcast to all waiters. | ||
| /// | ||
| /// This signals that all expected updates have been received. | ||
|
|
@@ -1032,6 +1062,21 @@ mod test { | |
| dynamic_filter.wait_complete().await; | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_reset_reopens_completed_filter() { | ||
| let dynamic_filter = | ||
| DynamicFilterPhysicalExpr::new(vec![], lit(false) as Arc<dyn PhysicalExpr>); | ||
| dynamic_filter.mark_complete(); | ||
| let generation_before_reset = dynamic_filter.inner.read().generation; | ||
|
|
||
| dynamic_filter.reset().unwrap(); | ||
|
|
||
| let inner = dynamic_filter.inner.read(); | ||
| assert!(!inner.is_complete); | ||
| assert!(inner.generation > generation_before_reset); | ||
| assert_eq!(inner.expr.to_string(), "true"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_with_new_children_independence() { | ||
| // Create a schema with columns a, b, c, d | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not reset the generation to 0? @jayzhan211 is it obvious to you? if it's not lets add a comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It isn't obvious, so a comment would help. The generation has to keep increasing across a reset.
current()caches the remapped expression by generation, so ifreset()went back to 1 (or 0), later updates would reach the cached generation again andcurrent()would return the previous run's expression. I tried it: with the generation reset to 1, the first update after the reset returns the old expression.@mikamikasuki could you add a comment like: