Skip to content

Eliminate zenoh teardown wedge at the source (don't just time it out) #2776

Description

@phil-opp

Summary

On node shutdown, tearing down zenoh state (Subscriber / Publisher / Session undeclare) blocks when the zenoh net runtime is wedged — e.g. stuck retrying an unreachable scouted peer. Today we only bound that hang with a timeout (ZENOH_TEARDOWN_TIMEOUT, added in #2439); we never fix the wedge itself. This issue tracks eliminating the wedge at its source so teardown completes promptly instead of being abandoned.

This is the defense-in-depth follow-up to the #2742 nightly regression. The immediate fix there lowers the teardown deadline below the daemon's force-kill grace so a wedged node exits cleanly rather than being force-killed (ExitCode(1) on Windows). That makes the symptom benign but leaves the wedge — and a leaked undeclare — in place.

Background

  • teardown_with_timeout runs the zenoh drop on a helper thread and blocks the main thread up to ZENOH_TEARDOWN_TIMEOUT (apis/rust/node/src/node/mod.rs), used by both DoraNode::drop and EventStream::drop.
  • The teardown blocks because Subscriber/Publisher/LivelinessToken/Session teardown does a synchronous undeclare on the shared session, which does not return while zenoh's net runtime is busy (scouting / retrying peers it can't reach). See the EventStream::drop comment (apis/rust/node/src/event_stream/mod.rs) and Nightly regression since 2026-06-30 #2425.
  • Arrow IPC data plane: official wire format, ≤1-copy send, schema-once #2366 (Arrow IPC data plane) added @schema AdvancedSubscribers / publishers on the same shared session, widening the surface that can wedge this way.
  • On headless CI runners there is no multicast and scouted peers are typically unreachable, which is exactly the condition that wedges the net runtime.

Why the timeout isn't enough

When the deadline fires we log a warning, abandon the (detached) teardown thread, and continue shutdown. That means:

  • the undeclare never happens → the subscription/liveliness token is leaked on the session until the process exits;
  • every shutdown under a partition pays up to the full deadline of latency;
  • the leaked detached thread is only reclaimed by process exit.

Fine as a safety net, wrong as the steady state.

Proposed directions (to investigate)

  1. Bound scouting / connect at the zenoh layer. Configure the session so the net runtime doesn't sit in an unbounded retry loop that blocks undeclare — e.g. tighter scouting/connect timeouts, or disable multicast scouting where we already know endpoints (cf. the dev-container workaround that sets explicit endpoints + exit_on_failure: false). Goal: undeclare returns promptly even with no reachable peers.
  2. Make teardown non-blocking by construction. If a clean undeclare can't be guaranteed to return quickly, undeclare best-effort / fire-and-forget so node Drop never blocks on the net runtime, rather than relying on a wall-clock deadline.
  3. Root-cause the zenoh undeclare block itself and, if it's a zenoh bug, file upstream / bump the pin.

Acceptance

  • A node whose zenoh session has only unreachable scouted peers tears down its subscribers/publishers/session in well under the current deadline (target: sub-second), with no leaked undeclare.
  • No dependence on ZENOH_TEARDOWN_TIMEOUT firing during a normal partitioned shutdown (the timeout stays only as a last-resort backstop).

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions