viirya opened a new issue, #25003:
URL: https://github.com/apache/datafusion/issues/25003

   ### Describe the bug
   
   In the coordinated memory-limited `NestedLoopJoinExec` fallback, dropping an 
unfinished output partition can strand the shared chunk and leave the other 
partitions waiting indefinitely.
   
   Chunk advancement requires every probing partition to report: the last one 
to call `JoinLeftData::report_probe_completed` becomes the emitter and releases 
the coordinator slot. A partition that goes away before finishing never 
reports, so no emitter is elected, nothing releases the slot, and a survivor 
asking for the next chunk finds
   
   - Case 1 does not match (the slot still holds the previous chunk),
   - Case 2 requires `current.is_none()`, so it does not fire,
   - Case 3 requires the same, so it does not fire,
   
   and falls through to the `notified()` wait in 
`FallbackCoordinator::next_chunk`, where nothing will ever wake it.
   
   There is a second effect on the same cause. The coordinator is owned by 
`NestedLoopJoinExec` rather than by the streams, so if the plan outlives them 
the slot keeps its `Arc<JoinLeftData>` — and since the reservation now lives 
inside `JoinLeftData`, the chunk stays charged against the memory pool for as 
long as the plan is alive.
   
   Two shapes reach this:
   
   1. **Release interrupted.** The emitter's release runs as a future stored in 
`chunk_release_in_flight` and polled across `poll_next` calls. If it is pending 
on the coordinator mutex when the stream is dropped, the future goes with it 
and the slot is never cleared.
   2. **No emitter at all.** If the streams are cancelled mid-probe, nobody 
drives the probe counter to zero, so `release_chunk` is never called in the 
first place. This is why making the release future cancellation-safe cannot fix 
the problem on its own.
   
   ### To Reproduce
   
   Driving the coordinator directly, with two nominal probe partitions so a 
single holder never drives the counter to zero:
   
   ```rust
   let coordinator = Arc::new(FallbackCoordinator::new(2, true));
   let spill = spill_left_for_test(build_left_table(), Arc::clone(&ctx)).await?;
   
   let (chunk, _) = Arc::clone(&coordinator)
       .next_chunk(0, Arc::clone(&spill), Arc::clone(&ctx), Time::new())
       .await?
       .expect("chunk 0");
   drop(chunk);                                    // partition cancelled 
mid-probe
   
   // A survivor asking for the next chunk never returns.
   tokio::time::timeout(
       Duration::from_secs(3),
       Arc::clone(&coordinator).next_chunk(1, spill, ctx, Time::new()),
   ).await                                          // Err(Elapsed)
   ```
   
   The retention half is observable as 505 bytes still reserved after every 
stream-side reference is gone.
   
   ### Expected behavior
   
   A partition disappearing before it finishes should not leave its peers 
waiting forever. The coordinated execution has lost a partition and can no 
longer produce a complete result, so the remaining partitions should fail 
rather than hang — or report success from partial input.
   
   ### Additional context
   
   Introduced with the coordinated fallback in #22038. It was found during that 
review and deliberately deferred: at the time only the memory retention was 
understood, which needs a caller that retains an `Arc<dyn ExecutionPlan>` 
across execution — something `DataFrame::collect` and `execute_stream` never 
do, since they build the plan as a local and drop it. The hang does not need 
that condition, which is why this is worth fixing rather than documenting.
   
   Note that the existing 
`datafusion.execution.enable_nlj_coordinated_fallback` opt-out does not avoid 
this in general: it disables the coordinated fallback only for multi-partition 
joins that require final left-side emission, so single-partition and other join 
types still take the coordinated path.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to