-
Type:
Task
-
Resolution: Fixed
-
Priority:
Major - P3
-
Affects Version/s: None
-
Component/s: None
-
None
-
Query Execution
-
Fully Compatible
-
QE 2026-05-11
-
None
-
None
-
None
-
None
-
None
-
None
-
None
Bug: Missing _innerOpened guard in `HashJoinStage`, `HashLookupStage`, and `HashLookupUnwindStage`
Root cause: All three stages follow the same pattern in open(): open the inner child, consume it into a hash table, close the inner child, then open the outer child. If an exception is
thrown during the build phase (e.g., `uassertStatusOK` failing the disk space check in `spillBufferedValueToDisk`, or a `WriteConflictException` propagating from the inner child's `getNext()`),
` innerChild() `is left open. The executor then calls `close()` via `PlanExecutorSBE::dispose()` — but `close() `only guarded the outer child with `_outerOpened`. The inner child was never
explicitly closed, leaking its cursor and held locks.
Why it went unnoticed: SERVER-124432 added the `_outerOpened` guard (outer = probe side, kept open through `getNext()`) but missed the symmetric `_innerOpened` guard (inner = build side,
opened and closed within `open()`). The build-side child is only exposed to exception paths, not the normal lifecycle. `AndHashStage` has the identical gap for its build-side child
(`_children[0]`), only guarding the probe side.
Fix: Three files each get the same treatment:
- `_innerOpened = true` immediately after `innerChild()->open()`
- `_innerOpened = false` immediately after `innerChild()->close() `
- In `close()`: close `innerChild()` if `_innerOpened` is set (before closing the outer child)
- is related to
-
SERVER-124432 protect top/down close for some stages in sbe
-
- Closed
-
- related to
-
SERVER-126341 Simplify SBE close()
-
- Closed
-