Claim only what the cascade pool can run

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UytviPMJbXzD8P84nLvXcq
This commit is contained in:
2026-08-25 18:33:28 +02:00
co-authored by Claude Opus 5
parent 68711e2ee7
commit df22475f54
2 changed files with 77 additions and 4 deletions
+24 -4
View File
@@ -1,9 +1,11 @@
"""The execution service: what turns journaled work into node runs.
A consumer thread claims items from the work queue and hands each one to a
dispatch pool, which drives the wave it starts. Node bodies run on a second,
separate pool: if cascade drivers and node bodies shared one, a wave waiting
for its own nodes could occupy every thread and deadlock.
dispatch pool, which drives the wave it starts. It claims only what that pool
can start, so a backlog waits in the queue rather than inside the process.
Node bodies run on a second, separate pool: if cascade drivers and node bodies
shared one, a wave waiting for its own nodes could occupy every thread and
deadlock.
A reaper takes back items claimed by an engine that died before acknowledging
them, which is the mechanism that makes a crash mid-cascade recoverable rather
@@ -134,8 +136,11 @@ class ExecutionService:
if not self._intake.is_set():
self._intake.wait(timeout=0.5)
continue
free = self._await_capacity()
if not free:
continue
try:
items = self.queue.claim(CLAIM_COUNT, CLAIM_BLOCK_MS)
items = self.queue.claim(min(CLAIM_COUNT, free), CLAIM_BLOCK_MS)
failures = 0
except Exception as exc:
failures += 1
@@ -185,6 +190,21 @@ class ExecutionService:
except Exception as exc:
logger.error("Could not reclaim stale work: %s", exc)
def _await_capacity(self) -> int:
"""How many cascades may be claimed now. Zero means the service stops.
Claiming past what the pool can run makes nothing faster: the extra
items queue up inside the pool, count as in flight and hold their
journal entries open the whole time, which is how four cascade threads
came to report hundreds busy on a healthy engine. Work left in the
stream is work that is still anyone's to take; work that is claimed is
work that is actually being run.
"""
with self._inflight_lock:
while self._inflight >= MAX_CASCADES and not self._stop.is_set():
self._inflight_lock.wait(0.5)
return 0 if self._stop.is_set() else MAX_CASCADES - self._inflight
def _dispatch(self, item: WorkItem) -> None:
with self._inflight_lock:
self._inflight += 1