docs(adr): ADR 009 makes every operator edge network-transparent, over up to two hops and request/response transports (improvise-felo.4)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
committed by
Edward Langley
parent
ee09edee0e
commit
f3a54d34e0
@@ -180,8 +180,34 @@ in their last bits, because they become correctly rounded, and a sum of `-0.0` r
|
||||
|
||||
### 9. Delivery
|
||||
|
||||
The same circuit serves the in-process TUI and remote clients. Its internal streams never
|
||||
cross a wire, because aggregation stays on the host.
|
||||
**Every edge may cross a network.** An edge, meaning the stream from one operator to the
|
||||
next, may be in-process or remote, and a path may cross up to two network hops: browser ⇄
|
||||
gateway ⇄ host, or host ⇄ worker ⇄ client. No edge assumes a function call or shared
|
||||
memory. An operator that sits at a wire boundary is just one implementation of an edge,
|
||||
an **edge adapter**. So every edge has the same semantics:
|
||||
- **A wire form.** Every element type that flows on an edge has a serializable form,
|
||||
defined in `improvise-protocol`. The adapter converts to and from it, and `zset` stays
|
||||
free of serde. `CellKey` is not `Serialize`, so on the wire a cell is named by its
|
||||
tuple of ids, its position or an ADR 002 surrogate.
|
||||
- **Stamps compose end to end.** The host stamps each published batch (below), and an
|
||||
intermediate edge forwards the stamp unchanged. So a loss anywhere on the path shows up
|
||||
downstream as a gap.
|
||||
- **Ordering and backpressure.** An edge delivers in order. A slow edge coalesces
|
||||
exactly: deltas are summed, and notices merged.
|
||||
- **No long-lived stream assumed.** An edge may be carried as a series of requests and
|
||||
responses, for example several HTTP requests on one connection, as well as a socket.
|
||||
So each edge keeps an ordered log, every request can be retried, and a resync from a
|
||||
publication stamp is idempotent.
|
||||
- **Failure is local to a hop.** A hop may be a socket or a series of requests, and it is
|
||||
alive while its next message or request succeeds. Each hop keeps alive in both
|
||||
directions. A side that hears nothing within the timeout, or whose request fails,
|
||||
treats the hop as dropped. The downstream side then resyncs: it refetches through the
|
||||
path, and the host answers from current state.
|
||||
|
||||
Aggregation stays on the model side (ADR 008), so the operators that aggregate run on
|
||||
the host. Which other edges cross a wire is a deployment choice. The engine's
|
||||
invalidation signal, which ADR 006's replay trigger relies on, is the change notice and
|
||||
the refetch rule below, followed from a displayed cell's key back to the model.
|
||||
|
||||
**A request** is a shape key plus a window, given as an absolute cross-product top-left
|
||||
and dimensions. The window is never state, and a request costs O(visible).
|
||||
@@ -227,11 +253,12 @@ identity, so every NaN counts as one value, because the wire spells every NaN `"
|
||||
state. `GridExtent` is derived by the client, and in records mode the record count is
|
||||
fetched.
|
||||
|
||||
**A connection operator** at the wire boundary adapts the circuit to a connection. It is
|
||||
a sink for results and notices and a source of requests toward the circuit, and the
|
||||
mirror image toward the client. It owns serialization, the stamp rules, notice
|
||||
coalescing, keep-alive in both directions, and session resumption. In process it is
|
||||
function calls. Its shape is open question 4.
|
||||
**Edge adapters at the client.** Toward the circuit, a client's adapter is a sink for
|
||||
results and notices and a source of requests. Toward the client it is the mirror image.
|
||||
Each hop has an adapter at each end, and each end keeps only that hop's state: the last
|
||||
stamp it applied and its liveness timer. A gateway hop (browser ⇄ gateway ⇄ host) may
|
||||
serve many clients behind one upstream connection, so the host sends one notice per
|
||||
gateway rather than one per client. In process, an edge is a function call.
|
||||
|
||||
### 10. Text column widths
|
||||
|
||||
@@ -290,7 +317,7 @@ Step 8's required tests:
|
||||
- a dropped notice is detected;
|
||||
- a reconnect after a host restart refetches;
|
||||
- a refused frame consumes no publication number;
|
||||
- a dropped connection forces a refetch.
|
||||
- a dropped hop forces a refetch.
|
||||
|
||||
## Open questions
|
||||
|
||||
@@ -299,7 +326,7 @@ Step 8's required tests:
|
||||
O(clients × all moved keys) per batch. Filtering by topic would send each connection
|
||||
only its own shape's key lists. It needs one subscription per connection, which pins
|
||||
its shape in the cache. Is such a subscription "model-relevant information" the
|
||||
backend may hold? It could also live in the connection operator (question 4).
|
||||
backend may hold? On a two-hop path the topics could instead live in a gateway.
|
||||
*Recommendation:* broadcast with the size cap. Add topics only if measured traffic
|
||||
with many clients on different shapes justifies them.
|
||||
|
||||
@@ -318,16 +345,7 @@ Step 8's required tests:
|
||||
small from-scratch reference implementation, kept in the test crate, as the oracle,
|
||||
and a pushdown backend behind the same trait for query-only sources.
|
||||
|
||||
4. **What shape is the connection operator?** Either one component per connection, or
|
||||
one per host process: a host half shared by all its connections, with a client half
|
||||
in every front end. The second lets the host send one notice per operator rather than
|
||||
per client, but holds per-connection state in the shared operator. Who owns
|
||||
resumption state, and is it backend state? *Recommendation:* one per host process,
|
||||
with resumption state in the host half keyed by a client-held token, treated as
|
||||
delivery state rather than UI state. In the TUI and the browser worker, with one
|
||||
client each, it is a function call.
|
||||
|
||||
5. **Which columns get a host-maintained width?** Columns whose cells are text? What
|
||||
4. **Which columns get a host-maintained width?** Columns whose cells are text? What
|
||||
about a column holding both text and numbers? *Recommendation:* text cells get the
|
||||
maintained width, and the view layer takes the larger of that and its own numeric
|
||||
width.
|
||||
|
||||
Reference in New Issue
Block a user