new tasks, devnet topup, cli, new model support
This commit is contained in:
51
docs/issues/37-relay-bridge-concurrency.md
Normal file
51
docs/issues/37-relay-bridge-concurrency.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# 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_request` and 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 by `request_id`).
|
||||
- `N` defaults to 8; configurable via `meshnet-node start --relay-concurrency N`
|
||||
(env `MESHNET_RELAY_CONCURRENCY`). Requests beyond `N` queue 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-request` to the same node completes without waiting for the first
|
||||
- Responses are correctly matched by `request_id` when they complete out of order
|
||||
- More than `N` simultaneous requests queue and all eventually complete; thread count
|
||||
never exceeds `N` workers
|
||||
- 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 pytest` passes from repo root
|
||||
Reference in New Issue
Block a user