Experiment benchmarks catalog delivery read findings - kmosoti/FabricO11y GitHub Wiki
Historical report imported 2026-10-10 from
docs/experiments/benchmarks/catalog-delivery-read-findings.md. Original SHA-256:eb60bad0a86045ac1dae39dcde2e3976386738ca49369bd5cd92a928bb00b947. Measurements and outcomes retain their stated revisions. Links and local environment labels are normalized;$FABRIC_DATAis the mounted experiment drive. See evidence policy.
Delivery work, observation cost and Batch ownership
The useful production change is ownership transfer at Spool append: collection
and trace commit hand over their Batch instead of deep-cloning its signal
payloads and source metadata. Validation, framing, sync ordering, ACK handling
and the public borrowed append API use their existing paths. This removes one
avoidable Batch copy; it does not establish a throughput or RSS percentage.
The owned-append controls (local checkout reference docs/experiments/benchmarks/catalog-owned-append-protocol.md; not present at origin/main) define verification.
All 11 targeted Spool tests exited 0 in the current closeout, including allocation
identity and exact bytes/reopen at 16/256/896 KiB, the borrowed negative control,
and malformed/oversized/full refusal without sequence/cursor advancement.
See targeted output (local evidence omitted from stage) and the
check receipt (local evidence omitted from stage).
What changed the investigation
The registered read-position screen (local checkout reference docs/experiments/benchmarks/catalog-delivery-read-protocol.md; not present at origin/main) tested
two competing explanations for weak collection/delivery overlap results.
catalog-delivery-read-01 exited 0 in 107.732 seconds. It ran 16 Spool controls
and six overlap controls with each compile selector, 18 release timing arms,
and four native TLS service arms. See its command receipt (local evidence omitted from stage),
child commands (local evidence omitted from stage),
source hashes (local evidence omitted from stage) and
comparisons (local evidence omitted from stage).
| Backlog service arm | Node CPU seconds | Source-to-ACK median | Observer scans / decoded Batches |
|---|---|---|---|
| Original repeated observer | 4.1814 | 247.34 ms | 2864 / 9304 |
| Baseline, final observer | 0.3575 | 221.96 ms | 1 / 4 |
| ACK-position candidate, final observer | 0.3504 | 209.96 ms | 1 / 4 |
| Baseline repeat, final observer | 0.3526 | 214.38 ms | 1 / 4 |
The observer ablation reduced node CPU by 91.45%, meeting its registered 25% criterion. Observation itself took 3.989 seconds versus 0.071 seconds. The previous observer scanned retained data even when collection was idle. Its work grew with both loop count and retained history; the final observer visits the finite retained fixture once. This explains a large instrumentation cost, not a production speedup. The final mode fails on a missing/reclaimed prefix and is unsuitable for an arbitrary long-lived Spool without a different observer.
Each service arm preserved all 512 offered logs, four ACKed Batches and three complete query chains. All 12 chains met the unchanged independent oracle; missing/duplicate query controls and changed-custody controls were rejected. No collection/delivery errors or retries occurred. Each accepted 2,048,000 logical log bytes. Absolute paths and metric values vary slightly between arms, so encoded Batch sizes are recorded separately. Sampled node RSS ranged from 8.65 to 11.13 MiB; this screen is too short and narrow for a capacity claim. Raw arms (local evidence omitted from stage) preserve CPU, I/O, RSS, acceptance totals, query results and enforced service limits.
Why the ACK cache was discarded
The candidate stored one validated next-frame position and avoided all 32 post-ACK payload rereads in each timing arm. At 896 KiB, median ACK wall ratios were 0.890, 0.924 and 0.944; at 256 KiB they were 1.115, 0.878 and 1.064. It missed the rule requiring at least 10% improvement in every large-payload pair. Whole-loop timing was also mixed. Durable ACK sync remains in both paths; the evidence does not establish how much of each residual cost belongs to sync. CPU tick resolution was coarse and is recorded as such in the probe results (local evidence omitted from stage).
The service candidate used 0.6–2.0% less CPU and had a 2.1–5.4% lower ACK median than the two final-observer baselines, but there is only one candidate service sample. That cannot rescue the failed repeated microbenchmark criterion.
An independent review also exposed a real error-observation difference: damage a committed frame after its validated delivery read but before ACK. Baseline ACK traversal detects that damage; the cached path can skip it. Both paths had already persisted the ACK marker, and reopen rejects the damaged frame. The paired deterministic control passed with that explicit difference. There is no reason to accept this trade-off for an unconfirmed speed benefit. The cache, compile selector and timing probe were removed from working implementation; their exact tested source (local evidence omitted from stage) and probe (local evidence omitted from stage) remain frozen with the results. Historical commands require that source snapshot, not the later owned-append tree.
Implementation selected
Rust ownership offers a narrower improvement. Both runtime callers constructed
a local Batch, called append(&candidate), then used only the returned committed
Batch. The borrowed method cloned every owned signal buffer, cursor path and
gap string. append_owned(candidate) moves these allocations into the already
existing by-value append implementation instead. On error the collector still
leaves source cursors/counter history unadvanced and records unknown coverage;
the trace caller still retains its exporter request for the existing error reply.
This is a representation and lifetime improvement, not a new wire format or mathematical algorithm. It avoids one full Batch clone while preserving all existing checks. Serialization, OTLP validation, source buffers and durable sync costs remain. Larger concurrency or adaptive batching should be evaluated only after measuring these remaining stages with an observer whose cost is separately accounted for.
Resources and closeout
The research job peaked at 651.68 MiB cgroup memory, with zero OOM/high/max events
and no swap. Sampled scratch peaked at 72.58 MiB, on the data drive. Its successful
scratch was removed, and all executable copies were checked against retained
archives before cleanup. Evidence occupied about 12.16 MiB including two node
archives and exact source snapshots. The unchanged conservative query evidence
inventory was 1,063,804,928 bytes after this job, below 1 GiB; later receipts add
to it. The launcher omitted its explicit delegation flag, although actual child
limits were successfully enforced and captured; the
follow-up protocol (local checkout reference docs/experiments/benchmarks/catalog-owned-append-protocol.md; not present at origin/main) records this command issue.
Closeout catalog-owned-append-checks-01 exited 0 in 267.631 seconds. The 11
targeted Spool tests, all 17 fast gates (cargo xtask checks --profile fast),
and three manual documentation gates (cargo xtask checks --profile documentation)
passed. The fast profile includes workspace tests/Clippy, layer/core-purity
checks, oracle controls, kernel properties, corpus checks and network simulation.
The linked check receipt records each child command/exit and exact final Rust
source hashes. No further Rust changes followed those checks.
The check job peaked at 7.58 GiB cgroup memory with no OOM, high/max events or
swap. Both launcher receipts confirm temporary removal and no retained failure
scratch. The unchanged evidence admission passed after closeout: aggregate
1,811,595,264 bytes, conservative query 1,065,590,784 bytes and capacity
746,004,480 bytes, before final receipt writes. The shared frontier has
2969.73 seconds remaining. These jobs do not change C5's separate preservation
limit or its unrun disposition.
The owned-path controls establish buffer reuse and exact durable behavior for their fixtures. They do not measure allocations made inside OTLP validation or encoding, and the oversized refusal case tests the field-size limit rather than every possible envelope-overhead boundary. An independent read-only review found no ownership or error-path regression. Production latency/throughput and RSS effects of this smaller change remain unmeasured; physical power loss and deployment qualification remain outside this run.