Skip to content

fix(#332): re-entrancy guard on daemon-direct delivery + probe loops (storm-2) - #347

Merged
tu11aa merged 1 commit into
developfrom
fix/332-delivery-reentrancy
Jun 16, 2026
Merged

fix(#332): re-entrancy guard on daemon-direct delivery + probe loops (storm-2)#347
tu11aa merged 1 commit into
developfrom
fix/332-delivery-reentrancy

Conversation

@tu11aa

@tu11aa tu11aa commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Fixes a SECOND notification storm found when live-testing daemon-direct (#332), plus the related control-plane-timeout.

Root cause: the daemon-direct delivery loop was setInterval(() => deliveryTick(), 1000) with NO re-entrancy guard. Each tick does slow cmux subprocess calls (findWorkspaceId + listSurfaces per project) and can exceed 1s, so the next interval fires while the previous tick is mid-flight → overlapping ticks read the same cursor seq and both deliver the entries after it → duplicate/storm. It also saturated the daemon event loop (back-to-back cmux spawns), which is why a crew's cockpit crew signal done timed out 3x.

Fix: closure-scoped re-entrancy guards (delivering / probing, set on entry, cleared in finally) on both the delivery loop and the blocked-crew probe loop — mirrors the relay drain()'s draining guard.

Parity audit: compared the daemon-direct delivery loop against the battle-tested relay drain() for ALL safeguards (we'd already hit 4 storm bugs that were each a drain() safeguard the daemon loop lacked). Re-entrancy was the last missing one; everything else (stale-skip, per-seq defer via CaptainDelivery, advance-only-on-delivered, null-message gate) is at parity. break-vs-return and the deliverable-gate location are correct multi-project adaptations, not bugs.

Test: overlapping-tick test (gate-blocked send; tick #2 fires while tick #1 in-flight) asserts each entry delivered EXACTLY ONCE — RED before, GREEN after. Verification: build clean, node dist/index.js --help loads, tsc clean, 39 tests pass (cockpitd-daemon-direct + relay-proxy + notify-relay, flag-OFF parity green).

This is the 4th and (per the drain-parity audit) final storm safeguard. Daemon-direct still ships flag-OFF (relay = default per the re-scope); this hardens it for opt-in.

Each deliveryTick does multiple slow cmux subprocess calls (findWorkspaceId
+ listSurfaces per project across ALL projects) and can exceed the 1s
interval, so the bare setInterval could fire again while the previous tick
was still in-flight. Two overlapping ticks read the SAME cursor seq and both
delivered the entries after it -> duplicate/storm delivery, and the
back-to-back cmux subprocess spawns saturated the daemon event loop (crew
signal timeouts).

Mirror the relay drain()'s 'draining' boolean: a closure-scoped guard set on
entry, cleared in a finally, skipping overlapping fires. deliveryTick wraps
deliveryCore with 'delivering'; probeTick (same hazard, slow pane reads on a
10s interval) gets its own independent 'probing' guard.

Test: two overlapping tickDelivery() calls (send blocked on a controllable
gate) deliver the entry exactly once; cursor advances once; a later tick does
not re-deliver.
@tu11aa
tu11aa merged commit 80823dc into develop Jun 16, 2026
1 check passed
@tu11aa
tu11aa deleted the fix/332-delivery-reentrancy branch June 16, 2026 16:39
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