This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-rust.git
The following commit(s) were added to refs/heads/main by this push:
new de19a3986 fix: avoid lost-wakeup hang in
get_equality_delete_predicate_for_delete_file_path (#3268)
de19a3986 is described below
commit de19a3986fbdf399f09f71cd8c08e2573630c80f
Author: Dhruv Arya <[email protected]>
AuthorDate: Thu Oct 1 08:00:37 2026 +0000
fix: avoid lost-wakeup hang in
get_equality_delete_predicate_for_delete_file_path (#3268)
* fix: avoid lost-wakeup hang in
get_equality_delete_predicate_for_delete_file_path
The equality-delete loader signals completion with Notify::notify_waiters(),
which stores no permit and only wakes Notified futures created before the
call.
get_equality_delete_predicate_for_delete_file_path observed the Loading
state,
released the read lock, and only then created the Notified future; a
notify_waiters() fired in that window is lost and the await hangs forever.
Create the Notified (via notified_owned) while still holding the read lock,
so
it is guaranteed to precede any notify_waiters() and be delivered.
Signed-off-by: Dhruv Arya <[email protected]>
* docs: explain why building Notified under the read lock prevents the lost
wakeup
State the mechanism rather than the outcome: notified_owned() snapshots
tokio's
notify_waiters_calls counter at construction, and holding the read lock
orders
that snapshot before insert_equality_delete can advance it. This keeps a
later
refactor from moving notified_owned() out from under the lock unnoticed.
Signed-off-by: Dhruv Arya <[email protected]>
---------
Signed-off-by: Dhruv Arya <[email protected]>
Co-authored-by: Dhruv Arya <[email protected]>
---
crates/iceberg/src/arrow/delete_filter.rs | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/crates/iceberg/src/arrow/delete_filter.rs
b/crates/iceberg/src/arrow/delete_filter.rs
index cef81afda..6f13be71d 100644
--- a/crates/iceberg/src/arrow/delete_filter.rs
+++ b/crates/iceberg/src/arrow/delete_filter.rs
@@ -168,17 +168,22 @@ impl DeleteFilter {
&self,
file_path: &str,
) -> Option<Predicate> {
- let notifier = {
+ // Build the `Notified` while holding the read lock.
`notified_owned()` records tokio's
+ // `notify_waiters_calls` counter at construction and completes on
first poll if that
+ // counter has since advanced. Reading the counter under the lock
guarantees it is taken
+ // before `insert_equality_delete` can advance it via
`notify_waiters()`, so the
+ // notification is never missed even though we `.await` after
releasing the lock.
+ let notified = {
match self.state.read().unwrap().equality_deletes.get(file_path) {
None => return None,
- Some(EqDelState::Loading(notifier)) => notifier.clone(),
+ Some(EqDelState::Loading(notifier)) =>
notifier.clone().notified_owned(),
Some(EqDelState::Loaded(predicate)) => {
return Some(predicate.clone());
}
}
};
- notifier.notified().await;
+ notified.await;
match self.state.read().unwrap().equality_deletes.get(file_path) {
Some(EqDelState::Loaded(predicate)) => Some(predicate.clone()),