diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-18 22:48:40 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-18 22:48:40 +0200 |
| commit | c066739551449bc8fea6a6ccc0793228fe0f907e (patch) | |
| tree | 2419ac396a420f1928cc8ad1361545653f5213e1 /docs/MESHBAY_DESIGN.md | |
| parent | af55e6bc052187855420c1d549659879e35d4a41 (diff) | |
| download | meshbay-c066739551449bc8fea6a6ccc0793228fe0f907e.tar.gz | |
fix(hub): Search waits on a connection that stalls, not on one that is slow
Opening a group from the sidebar has no deadline of its own and gets the
transport's: 30 s for the DataChannel, 30 s for each request after it. Search
wrapped the same `connect()` in a flat 10 s, and that 10 s had to cover the hub
round trip, ICE gathering (capped at 4 s in transport.js), DTLS, the channel
opening and the handshake's own round trips. On a phone on 4G the budget was met
by luck rather than margin, and the same group then failed in Search while it
opened from the sidebar, against the same node. The budgets were inverted: the
phase full of round trips had a third of what one request on an open channel got.
Raising the number would have been the wrong repair. `fetchAllIndexes` fans out
three at a time and waits for the slowest of each batch, so a page of unreachable
groups costs batches x the deadline in spinner: a bigger number taxes every dead
group for the sake of the live ones.
So the deadline measures stalling. A node that is not there reports nothing and
still fails in `SEARCH_STALL_MS`, unchanged at 10 s, which is what keeps the
fan-out where it was. A node that answers ICE, then opens a channel, buys another
window at each step, up to `SEARCH_MAX_MS` — a deadline that only ever resets has
none, and a node that answers and then goes quiet would otherwise never be given
up on.
The transport reports those steps through `onConnectProgress`, set by the one
caller that imposes a deadline of its own. `connected`/`completed` is the signal
and not `checking`, because the first means a candidate pair answered and the
second means this side is still trying addresses that may all be dead. A caller's
callback cannot break the connection it is reporting on.
The tests run the shipped `connectToGroup`, lifted out as text, against a fake
clock — a real one would make each scenario a minute and blur the only thing
worth asserting, which is when the deadline fires. Dead node: 10 s. Slow but
moving: connects at 20 s where it used to fail at 10. Answers then stops: 18 s.
Progress that never finishes: the 30 s ceiling. Two dead nodes: two windows, both
transports closed. Checked against the flat deadline, which fails three of them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'docs/MESHBAY_DESIGN.md')
| -rw-r--r-- | docs/MESHBAY_DESIGN.md | 12 |
1 files changed, 12 insertions, 0 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md index 92c3021..b3b44b3 100644 --- a/docs/MESHBAY_DESIGN.md +++ b/docs/MESHBAY_DESIGN.md @@ -2504,6 +2504,18 @@ Two rules for a new application here: function. A copy keeps agreeing until one of them changes, and the symptom is a show whose episodes stream from two different nodes. +**Search dials many nodes at once, so its deadline measures stalling rather than +elapsed time.** Opening one group from the sidebar waits out the transport's own +budgets; Search opens up to a poolful, three at a time, and waits for the slowest +of each batch — so the deadline it imposes is what a page of unreachable groups +costs in spinner, and raising it to accommodate a slow link taxes every dead one. +A node that is not there reports no progress and fails in one window. A node that +answers ICE, then opens a channel, buys another window at each step, to a ceiling, +because a deadline that only ever resets has none. A flat budget cannot tell a +slow link from a node that is not answering, and set low enough for the fan-out it +cuts off connections that are still arriving: the same group then fails in Search +and opens from the sidebar, against the same node. + **The Files explorer is deliberately not merged**, and not "mostly not": there each group is a top-level folder, and merging would remove a file from one of them. A test refuses a build that changes this. |