Repository navigation
Description
Background
PR #2085 (merged) reworked dataflow restart into a two-phase, event-driven flow to fix the cross-daemon Zenoh race in #2082:
- Phase 1 (
initiate_restart) sendsStopDataflowto all daemons, persistsStopping, and parks aPendingRestart(descriptor + name + uv + the caller'sreply_sender) inpending_restarts. - Phase 2 completes inside the
DataflowFinishedOnDaemonhandler when the last daemon reports finish, callingstart_dataflowand firing the parked reply.
The core fix is correct. This issue tracks the follow-up gaps that PR #2085 left behind (one of them already flagged as an inline TODO).
Problem 1 — caller hangs forever if a daemon dies mid-restart (primary)
If a daemon crashes/disconnects after receiving StopDataflow but before sending DataflowFinishedOnDaemon, the PendingRestart entry is never removed and its reply_sender never fires. The restart caller (CLI) blocks indefinitely with no error.
This is a behavioral regression relative to the old synchronous restart_dataflow, which surfaced stop/start failures immediately. The two-phase flow trades a visible error for a silent hang.
The PR documents this as an inline TODO in initiate_restart:
// TODO: If a daemon crashes after receiving StopDataflow but before
// sending DataflowFinishedOnDaemon, this entry (and its reply_sender)
// will never be cleaned up, causing the restart caller to hang
// indefinitely. A timeout or a daemon-disconnect cleanup path should
// be added to evict stale PendingRestart entries.Proposed fix: evict stale PendingRestart entries and reply Err when their restart can no longer complete — either via a timeout, or (preferred) by draining matching pending_restarts entries in the daemon-disconnect cleanup path so the caller gets a clear error.
Problem 2 — double-restart guard runs after StopDataflow (minor)
In initiate_restart, the pending_restarts.contains_key(...) guard is checked after stop_dataflow has already broadcast StopDataflow to every daemon. A duplicate Restart for an already-restarting dataflow therefore incurs a redundant stop round-trip before being rejected. The guard should be moved ahead of the stop_dataflow call (right after extracting the descriptor) so duplicates are rejected before any daemon I/O.
Problem 3 — no coordinator-side test for the new restart path (minor)
The new two-phase restart shipped without a coordinator test. The race itself is hard to reproduce deterministically, but the double-restart guard and the deferred-completion → reply wiring are both testable in binaries/coordinator/tests/ without real daemons. Adding coverage there would lock in the guard behavior and the cleanup path from Problem 1.
References
- PR fix(coordinator): two-phase restart to prevent cross-daemon Zenoh data-plane race after dataflow restart #2085 (the two-phase restart fix)
- Nightly regression since 2026-06-07 #2082 (the original flaky cluster-e2e race)
Activity
phil-opp commentedon Jun 16, 2026
Investigating this as part of an automated issue review pass — the three problems described here all appear to already be resolved in the current
main:Problem 1 (caller hangs on daemon crash mid-restart):
cleanup_disconnected_daemons_from_running_dataflowsalready drainspending_restartsfor all affected dataflows and fires thereply_senderwith an error (coordinator/src/lib.rs:3046–3058). The inline TODO is gone.Problem 2 (double-restart guard runs after StopDataflow):
initiate_restartalready checkspending_restarts.contains_key(&dataflow_uuid)as the first thing it does, before anystop_dataflowcall (coordinator/src/lib.rs:4624–4631).Problem 3 (no coordinator test): Two tests exist:
cleanup_disconnected_daemons_drains_pending_restarts— verifies the stale entry is removed and the caller is released with an error (lib.rs:7833)initiate_restart_rejects_duplicate_request— verifies the double-restart guard (lib.rs:7881)Both pass under
cargo test -p dora-coordinator. This issue can likely be closed unless there are additional cases not captured here.Generated by Claude Code
phil-opp commentedon Jun 18, 2026
After reviewing the current codebase, both problems described here are already addressed:
Problem 1 — daemon crash during restart hangs caller
Fixed in
binaries/coordinator/src/lib.rsaround line 3046 (cleanup_disconnected_daemons_from_running_dataflows). When a daemon disconnects, the function drainspending_restartsfor every affected dataflow UUID and firesreply_senderwith an error:This is invoked from the
Event::DaemonExithandler (around line 2209), so a daemon crash/disconnect during Phase 1 now returns an error to the CLI caller instead of hanging.Problem 2 — double-restart guard
Present at
binaries/coordinator/src/lib.rsaround line 4624, placed before any side effects ininitiate_restart:Tests
Both behaviors are covered by unit tests in the same file:
cleanup_disconnected_daemons_drains_pending_restarts(Problem 1)initiate_restart_rejects_duplicate_request(Problem 2)It looks like these fixes landed after this issue was filed. This issue can be closed.
Generated by Claude Code
phil-opp commentedon Jun 22, 2026
All three problems described in this issue have been addressed in the codebase:
Problem 1 (caller hangs on daemon disconnect):
cleanup_disconnected_daemons_from_running_dataflowsnow drains matchingpending_restartsentries and fires the reply with an error when a daemon disconnects mid-restart (lib.rs:3046–3059). The inline TODO referenced in the issue has been removed.Problem 2 (double-restart guard fires after StopDataflow): The
pending_restarts.contains_key(...)guard was moved to the top ofinitiate_restart, beforestop_dataflowis called (lib.rs:4626–4631).Problem 3 (no test): Two unit tests cover the new behavior:
cleanup_disconnected_daemons_drains_pending_restarts(lib.rs:7833) — verifies the caller receives anErron daemon disconnect.initiate_restart_rejects_duplicate_request(lib.rs:7881) — verifies duplicate restarts are rejected before any daemon I/O.This issue can be closed.
Generated by Claude Code
phil-opp commentedon Jun 27, 2026
After reviewing the current codebase, all three problems described in this issue appear to already be fixed:
Problem 1 (caller hangs forever on daemon disconnect) — Fixed in
cleanup_disconnected_daemons_from_running_dataflows(binaries/coordinator/src/lib.rs). When a daemon disconnects, the function now drains any matchingpending_restartsentry and fires the reply sender with an error:Problem 2 (double-restart guard runs after StopDataflow) — Fixed in
initiate_restart. Thepending_restarts.contains_key(...)guard is now checked at the top of the function, before any daemon I/O:Problem 3 (no coordinator-side test) — Two tests now cover these paths:
cleanup_disconnected_daemons_drains_pending_restartsandinitiate_restart_rejects_duplicate_request.Closing as already resolved.
Generated by Claude Code
phil-opp commentedon Jul 10, 2026
All three problems in this issue appear to be already resolved on
main(checked atcaf62a0) — no code change is needed. Details:Problem 1 (primary) — caller hangs if a daemon dies mid-restart: the daemon-disconnect cleanup now drains matching
pending_restartsentries and repliesErrso the caller gets a clear error instead of hanging. Seecleanup_disconnected_daemons_from_running_dataflowsinbinaries/coordinator/src/lib.rs:The inline
TODOreferenced in the issue is gone.Problem 2 — double-restart guard runs after
StopDataflow: ininitiate_restartthepending_restarts.contains_key(...)guard is now checked first, before extracting the descriptor and beforestop_dataflow, so a duplicateRestartis rejected before any daemon I/O:Problem 3 — no coordinator-side test: coverage now exists in
binaries/coordinator/src/lib.rstests:cleanup_disconnected_daemons_drains_pending_restarts(locks in the Problem 1 cleanup path)initiate_restart_rejects_duplicate_request(locks in the Problem 2 guard)This issue looks safe to close. Flagging rather than opening a PR since there's nothing left to change.
Filed automatically by a Claude code-review check; please verify before closing.
Generated by Claude Code
test(coordinator): cover pending-restart cleanup on daemon disconnect
test(coordinator): cover pending-restart cleanup on daemon disconnect