2.7 KiB
US-037 — Concurrent request handling in the node relay bridge
Status: planned Priority: Critical (blocks public friends-test deployment) Stage: Designed
Context
RelayHttpBridge._run (packages/node/meshnet_node/relay_bridge.py) handles
relay-http-request envelopes serially inside its recv loop: _handle_request blocks
on urllib.request.urlopen(..., timeout=300) before the next envelope is read.
Off-LAN this is a correctness bug, not just a throughput limit: a node can be the
head of one inference route and a downstream hop of another at the same time.
While the head request occupies the bridge (up to 300 s of generation), the other
route's per-token /forward calls sit unread in the WebSocket buffer — overlapping
routes through a shared node are effectively broken.
Decision (grilled 2026-07-06): dispatch requests on a bounded worker pool, default
8, configurable. Rejected alternatives: unbounded thread-per-request (a public
deployment exposes volunteer machines to request stampedes) and reject-when-full
(bouncing /forward hops kills other routes' in-flight sessions; queueing beyond the
cap is today's behavior, just 8-wide).
Design
- Recv loop only parses envelopes and submits to a
ThreadPoolExecutor(max_workers=N); workers run_handle_requestand send the response frame(s). - All sends on the single relay WebSocket go through a
threading.Lock. With US-036 a response may be multiple frames — the lock is held per frame, so streams from concurrent requests interleave frame-atomically (receivers demux byrequest_id). Ndefaults to 8; configurable viameshnet-node start --relay-concurrency N(envMESHNET_RELAY_CONCURRENCY). Requests beyondNqueue in the executor.- On reconnect (
_run's outer loop), in-flight workers from the dead connection may still try to send; the send helper swallows failures on a closed socket, and the relay side times the orphaned request out (US-036 idle timeout / existing 310 s cap). stop()shuts the executor down without waiting for stragglers (daemon threads, same as today's bridge thread).
Acceptance criteria
- While one relayed request is in flight (slow local handler), a second
relay-http-requestto the same node completes without waiting for the first - Responses are correctly matched by
request_idwhen they complete out of order - More than
Nsimultaneous requests queue and all eventually complete; thread count never exceedsNworkers - Bridge survives a relay reconnect with workers still in flight (no crash, no deadlock; orphaned responses dropped)
- Extend
tests/test_gossip_and_relay.py python -m pytestpasses from repo root