diff --git a/context/adr/009-incremental-evaluation.md b/context/adr/009-incremental-evaluation.md index 794658c7..c854b1aa 100644 --- a/context/adr/009-incremental-evaluation.md +++ b/context/adr/009-incremental-evaluation.md @@ -140,18 +140,19 @@ aggregates are keyed by axis **items**, so a collapse, hide, show, reorder or ad changes no operator. The visible axis is a remap. - **A new key** builds its operators as a sliced job under ADR 008's job runner. Deltas - arriving during the build are buffered and added at install. + arriving during the build are buffered and added at install. The finished result is the + response to the request that asked for it, not a notice. - **A formula edit** is a formula delta fed to the circuit. - **A document swap** rebuilds everything. - **Saving a view definition** touches no operator. - **Live keys are a cache.** A key is evicted after a period in which no request names it (the period is set by measurement), and every eviction is announced (clause 9). -**Column totals and hidden rows.** This holds if hidden items travel in a request, which -is ADR 008's open question. If they do not, totals are over all rows. A total pools only -the drawn rows, so a hidden row leaves it. The shape key is shared and a hide is one -client's, so totals are kept over all rows. A read subtracts the request's H hidden cells -as a temporary overlay: +**Column totals and hidden rows.** Totals are kept over all rows, because the shape key +is shared and a hide is one client's. Whether hidden items travel in a request is ADR +008's open question. If they do not, a total is read as it is kept. If they do, a total +must leave the hidden rows out, so a read subtracts the request's H hidden cells as a +temporary overlay: - `Count` subtracts; - `Min` and `Max` skip keys whose whole count is hidden; - text counts the distinct keys left; @@ -218,8 +219,8 @@ an **edge adapter**. So every edge has the same semantics: was lost. - **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. - Every request can be retried, and a resync from a publication stamp is idempotent. A - client asks for "notices since n", and the host answers from **one shared, bounded log + Every request can be retried, and a resync from a watermark is idempotent. A client + asks for "notices after my watermark", and the host answers from **one shared, bounded log of published notices**: a single log, not one per edge or client, holding the notices themselves (keys and flags, not values). A request older than the log's start is answered with a correction (below). @@ -238,18 +239,23 @@ an **edge adapter**. So every edge has the same semantics: applied. A watermark from another host instance or generation is behind every current stamp. The client holds one per shape key it reads. The host and any gateway keep no watermarks: a client sends its own with each request. - - where the shared log covers the watermark, the host sends the current values of the - keys the log lists as moved since then, restricted to the client's window, and the - client applies `target − held` over those keys. The log holds keys and flags; values - are always read from the model's current state, so nothing per client is kept; - - otherwise the host sends `target` for the whole window, and the client applies - `target − held`; + - where the shared log covers the watermark and the covered range holds only moved + keys, the host sends the current values of those keys, restricted to the client's + window, and the client applies `target − held` over them. The log holds keys and + flags, not values, so nothing per client is kept; + - where the covered range holds an axis change, a relabel or a flag, which position + and labels depend on, the host sends `target` for the whole window; + - where the log no longer covers the watermark, the host sends `target` for the whole + window, and the client applies `target − held`; - a full refetch is `target − ∅`. - A correction is labelled with the publication range it covers, so it is applied only at - the watermark it starts from, which makes it idempotent and resumable. Its elements are - `(key, value)` pairs with weights, in the lossless wire form, so a client, a gateway or a - worker applies it with the same code. + **Every read sees only the last committed state**, whether it is a correction, a + fetch or `project`, and it is labelled with that state's stamp. A value a failed + command wrote and then rolled back is never read. A correction is labelled with the + publication range it covers, so it is applied only at the watermark it starts from, + which makes it idempotent and resumable. Its elements are `(key, value)` pairs with + weights, in the lossless wire form, so a client, a gateway or a worker applies it with + the same code. Aggregation stays on the model side (ADR 008), so the operators that aggregate run on the model side, which may itself span a hop. Which other edges cross a wire is a @@ -384,7 +390,7 @@ Step 8's required tests: 1. **Should notices be filtered per client?** Clause 9 sends every client every live shape's moved keys. That keeps the host free of per-client state, but traffic is - O(clients × all moved keys) per batch. Filtering by topic would send each connection + O(clients × all moved keys) per committed command. 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? On a two-hop path the topics could instead live in a gateway.