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_DATA is 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.