101 lines
4.8 KiB
Markdown
101 lines
4.8 KiB
Markdown
---
|
|
id: ops-fetch
|
|
name: blobs/fetch — the verified Sub op and the fanout seam's consumer side
|
|
status: pending
|
|
depends_on: [ops-foundation, ops-call-harness]
|
|
scope: moderate
|
|
risk: medium
|
|
impact: component
|
|
level: implementation
|
|
tags: [wave-7, ops, fetch, fanout, verification]
|
|
---
|
|
|
|
## Description
|
|
|
|
`blobs/fetch` (ops-surface.md §Op family) — the server→client
|
|
streaming op: `{namespace, digest, ranges?}` in; **verified bytes
|
|
out** over the binary data channel.
|
|
|
|
- **The op shape**: `OperationType::Sub` (alkcall ADR-021's streaming
|
|
handler kind) with the **`channel_open` marker** (alkcall ADR-047)
|
|
— the binary data plane rides a `channel_open`-allocated channel;
|
|
the establishment phase (alkcall ADR-049) carries the fetch's ack
|
|
shape in the `Establishment` reply fields as the wire requires. The
|
|
**read** action (ACL exactly like stat/have — closed-by-default).
|
|
- **Verified streaming** (ADR-006; ops-surface.md): the *consumer*
|
|
hash-checks the received whole against the carried digest —
|
|
whole-blob verification; there are no per-chunk or per-range checks
|
|
derivable under the canonical digest. The handler streams from the
|
|
facade only: `Store::get` (the whole) or `Store::read_range` (the
|
|
ranged form). **Ranged fetch** (`ranges?`): the response carries
|
|
the same ADR-006 slice-digest convention `read_range` returns —
|
|
SHA-256 over exactly the returned bytes, out-of-band.
|
|
- **The fanout seam's consumer side** (ops-surface.md; POC #3 finding
|
|
A3): the fetch handler consults the foundation's fanout registry —
|
|
a live broadcast for the digest → a **subscriber arm** taps it; no
|
|
live broadcast, or one that ended (a **late joiner**) → the normal
|
|
post-commit verified `get` — identical bytes under CAS. The
|
|
slow-subscriber policy (drop-and-late-join) is ops-layer policy,
|
|
not a store concern. Named design point: **the producer side's
|
|
shape** — the put handler's stream reader as the canonical producer
|
|
(serve-while-persisting, A3's shape), and whether fetches also
|
|
produce read-side broadcasts (concurrent fetchers of one stored
|
|
digest sharing one store read) — decide and record against the A3
|
|
text.
|
|
- **The miss shape**: a fetch of an absent digest is the typed
|
|
missing-error schema (the need announcement *is* the fetch miss —
|
|
the have/need diff is caller-side; no separate need op exists).
|
|
- **Tests through the harness**: the whole-blob verified round-trip
|
|
(byte-exact; the client's hash-check passes); the ranged form
|
|
(bytes + slice digest matching `read_range`'s convention); the
|
|
fanout consumer side (a live broadcast tapped — the subscriber
|
|
receives while the producer runs; a late joiner degrades to the
|
|
get path — asserted by which path served); the miss shape; the ACL
|
|
denial (no read grant on the namespace → denied).
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] fetch exported as a Sub spec/handler pair with the
|
|
`channel_open` marker + ALPN; the establishment reply fields
|
|
carry the specced ack shape
|
|
- [ ] Whole-blob verification: the received whole hash-checks against
|
|
the carried digest (the consumer-side discipline documented on
|
|
the op; the server streams verified-store bytes)
|
|
- [ ] Ranged fetch carries the ADR-006 slice-digest convention
|
|
(out-of-band, over exactly the returned bytes)
|
|
- [ ] The fanout consumer side: a live broadcast → subscriber arm; a
|
|
late joiner → the verified get path; the producer-side design
|
|
point decided and recorded
|
|
- [ ] The miss shape is the typed missing error; the have/need diff
|
|
stays caller-side (no need op invented)
|
|
- [ ] ACL: the read action; closed-by-default verified by test
|
|
- [ ] Handlers touch only the store facade (no backend side-door)
|
|
- [ ] Harness tests green; `cargo test` (default) green;
|
|
`cargo test --features ops` green; clippy/fmt green on both
|
|
shapes; `cargo doc --no-deps` clean
|
|
|
|
## References
|
|
|
|
- docs/architecture/ops-surface.md (§Op family — the fetch bullet +
|
|
the fanout text; the need-half note)
|
|
- docs/architecture/decisions/006-verification-posture-and-transfer-encoding.md
|
|
(whole-blob verification; the slice-digest convention)
|
|
- docs/architecture/decisions/008-trait-size-probe-fleet-gc-and-large-engines.md
|
|
§3 (the re-routing topology this op serves — large-blob access
|
|
through a storage node)
|
|
- docs/research/poc-largeblob-findings.md finding A3 (the fanout
|
|
seam: one reader, store arm + subscriber arms; late joiners
|
|
degrade to get-after-commit)
|
|
- alkcall (upstream checkout): ADR-021 (streaming handler for
|
|
subscriptions), ADR-046/047/049 (the sink kind, the channel_open
|
|
marker, the establishment phase), `src/channels/client.rs`
|
|
- tasks/ops-foundation.md (the fanout registry's mechanics),
|
|
tasks/ops-call-harness.md (the harness)
|
|
|
|
## Notes
|
|
|
|
> To be filled by implementation agent
|
|
|
|
## Summary
|
|
|
|
> To be filled on completion |