Skip to content

Kmontemayor/graph store event wake#720

Draft
kmontemayor2-sc wants to merge 8 commits into
mainfrom
kmontemayor/graph-store-event-wake
Draft

Kmontemayor/graph store event wake#720
kmontemayor2-sc wants to merge 8 commits into
mainfrom
kmontemayor/graph-store-event-wake

Conversation

@kmontemayor2-sc

Copy link
Copy Markdown
Collaborator

Scope of work done

Where is the documentation for this feature?: N/A

Did you add automated tests or write a test plan?

Updated Changelog.md? NO

Ready for code review?: NO

kmontemayor and others added 5 commits July 23, 2026 17:15
Add a per-channel in-flight submission cap to the shared graph-store
sampling worker's scheduler so the single scheduler thread stays
wait-free at high fan-out.

- worker_concurrency loop-local mirrors the sampler's bounded semaphore.
- _is_channel_parked_locked: a channel with worker_concurrency batches
  submitted-but-not-completed is "parked".
- _submit_one_batch PARKs (returns False, no re-enqueue) instead of
  issuing a submit that could block the scheduler on a saturated output
  channel; re-enqueue now goes through the membership-guarded helper to
  avoid a duplicate round-robin turn.
- _on_batch_done WAKEs a parked channel by re-enqueueing it once an
  in-flight slot frees -- the sole wake path.
- Phase 3 idle wait, both worker/backend signatures, and the plain
  round-robin pump body are unchanged from main.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the GLT-order-faithful regression tests that guard the per-channel
in-flight cap against the cross-rank deadlock it fixes. The shared harness
(_BoundedBlockingChannel, _GltOrderFakeSampler, _CountingTaskQueue) drives the
real _shared_sampling_worker_loop with a paused consumer whose channel
saturates and whose coroutines wedge in send, holding the sampler semaphore.
StallFixWorkerLoopTest then asserts the co-located active channel keeps
completing epochs and commands keep draining, and that a parked channel idles
on Phase 3's timed wait instead of busy-spinning. Also give the existing
worker-loop base test a real int worker_concurrency so the parked-channel
check does not compare against a MagicMock.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The per-server scheduler's Phase-3 idle wait blocked on
task_queue.get(timeout); a batch completion re-enqueued a parked channel
but could not interrupt that sleep, so a freed sampler slot idled up to
one SCHEDULER_TICK_SECS (~50ms, mean ~25ms). Add a loop-local
threading.Event: _on_batch_done set()s it (under state_lock,
non-blocking, never task_queue.put) right after the WAKE re-enqueue;
Phase 3 waits on the event with the existing tick as fallback, then
loops back so Phase 1 drains commands via get_nowait().

The Event is level-triggered and set() happens under the same state_lock
hold as the WAKE re-enqueue, so no wakeup is lost. Command handling moves
entirely to Phase 1; worst-case command latency is one fallback tick,
negligible at per-epoch cadence. PARK/WAKE/in-flight-cap and the
sampler-call-outside-state_lock invariant are unchanged; the stall fix is
not reopened.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 3 now waits on the completion wake_event instead of task_queue, so a
parked scheduler issues ZERO blocking task_queue.get calls. Flip
test_unregister_while_parked_tears_down_cleanly's Phase-3 assertion to the
event-wake form (blocking_gets == 0; still a handful of get_nowait, not a
busy-spin) and reframe the neighboring comment.

Add test_completion_event_wakes_parked_channel_without_tick: with
SCHEDULER_TICK_SECS patched to 30s and worker_concurrency=1, a withheld-callback
sampler parks the single channel; firing the completion must refill the freed
slot within 5s via the event (far under the 30s fallback tick), with
task_queue.blocking_get_calls == 0 throughout. Non-vacuous only with the
event-wake: without wake_event.set() the second submit waits the full 30s and
times out.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fix a shutdown hang reachable once event-wake moved command handling into
Phase 1. A parked channel's in-flight batch could complete (re-enqueue +
wake_event.set()), a STOP arrive, Phase 3 wake, Phase 1 drain STOP, and
Phase 2 still submit a fresh batch on the unparked channel whose channel.send
blocks on a full output channel at teardown — wedging finally's wait_all().

Add an early `if not keep_running: break` immediately after the Phase-1 drain
and before Phase 2 so the STOP iteration never pumps again. Teardown is handled
by the finally block and the separate unregister protocol, so no final pump is
needed. Only the STOP path is affected (keep_running only goes False on STOP).

Strengthen test_completion_event_wakes_parked_channel_without_tick: the fake
sampler now tracks submit_count, and the test asserts exactly 2 submits after
shutdown (no 3rd batch after STOP). This fails without the break (3 != 2).

Update the stale _CountingTaskQueue docstring to describe PR-2's wake_event
Phase-3 wait instead of the old blocking task_queue.get.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kmontemayor2-sc

Copy link
Copy Markdown
Collaborator Author

/all_test

@github-actions

Copy link
Copy Markdown
Contributor

GiGL Automation

@ 18:25:39UTC : 🔄 Integration Test started.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

GiGL Automation

@ 18:25:39UTC : 🔄 Lint Test started.

@ 18:33:26UTC : ✅ Workflow completed successfully.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

GiGL Automation

@ 18:27:33UTC : 🔄 Scala Unit Test started.

@ 18:38:54UTC : ✅ Workflow completed successfully.

@github-actions

Copy link
Copy Markdown
Contributor

GiGL Automation

@ 18:33:47UTC : 🔄 E2E Test started.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

GiGL Automation

@ 18:36:14UTC : 🔄 C++ Unit Test started.

@ 18:38:05UTC : ✅ Workflow completed successfully.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

GiGL Automation

@ 18:38:07UTC : 🔄 Python Unit Test started.

@ 19:45:11UTC : ✅ Workflow completed successfully.

kmontemayor and others added 3 commits July 24, 2026 17:35
Apply comment/docstring review feedback (humanify): reword the top in-flight-cap
block to a standing property (drop the change-story "core of the stall fix"),
remove the "(fix step 4)" plan reference from the finalization TODO, and cut the
semaphore-block rationale that was restated in the PARK inline comment and the
_pump_runnable_channel_ids docstring down to pointers at the canonical top block.
No logic change.

Add test_parked_channel_resumes_and_completes_when_consumer_drains (ace-it): the
existing tests only park a channel; none resumes one. This pins the WAKE path
directly -- park a channel by pausing its consumer, then resume and assert the
epoch completes (EPOCH_DONE + every batch drained). Verified non-vacuous: with
the _on_batch_done re-enqueue disabled the test fails at the epoch-done get
(10s timeout), since the pump never revisits a channel it parked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
EPOCH_DONE fires when the last batch is sent (buffered), not when the consumer
has received it, so asserting channel.total_received == total_batches immediately
after the epoch-done event races the consumer draining the tail.  Poll for the
count instead.  submit_count == total_batches stays an immediate assertion --
it is deterministic once EPOCH_DONE fires (all batches completed => all
submitted).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ph-store-event-wake

# Conflicts:
#	tests/unit/distributed/graph_store/shared_dist_sampling_producer_test.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant