holger Vec<u8> → Bytes migration plan (zero-copy fetch path)
Shovel-ready plan for the audit HIGH "core trait returns owned
Vec<u8>".**= not yet done.
The seam
RepositoryBackendTrait::fetch (traits/src/lib.rs:160) returns Option<Vec<u8>>; change to Option<bytes::Bytes>.
Bytesderefs to&[u8], so every existing&[u8]/as_deref()reader keeps working unchanged.put(&self, &[u8])stays —Bytesderefs in.- Also:
fetch_many_with_upstreams→HashMap<ArtifactId, Bytes>;HolgerObject::fetch(traits:301) for symmetry.bytesis already in the tree via prost.
Why it's worth it
- Hot path:
grpc.rsstream_artifact(~391–434) currently doesBytes::from(data)thenbuf.slice(..)per chunk — withfetch→Bytesthe conversion at L421 disappears (already slices Bytes). Unaryfetch_artifactproto field is alreadyprost::bytes::Bytes. - Proxy/cache + downstream clones become cheap ref-count moves.
- Caveat: znippy
extract_filestill copies once at decompress (unavoidable) →Bytes::from(vec); the win is downstream zero-copy, not the extraction.
Blast radius (26 impls, ~25 call sites)
- 14 znippy backends (znippy-rust/npm/docker/python/maven/gem/go/deb/rpm/helm/nuget/composer/conda/package): wrap the existing extraction in
Bytes::from(vec). - 2 file (file-rust:368
fs::read, file-oci:294):Bytes::from(vec). - 3 proxy/wrapper (proxy.rs:47–75 ProxyBackend + 2 test stubs): no logic change — moves transparent,
put(&data)derefs. - 1 upstream (RustRegistryUpstream), 6 test/mock.
Safe landing order
- Trait seam (
fetch/fetch_many/HolgerObject::fetch→Bytes). - Hot path first:
RustRepoZnippy::fetch→Bytes+ drop thegrpc.rsstream conversion; run tests. - Remaining 13 znippy backends.
- file-rust + file-oci.
- proxy + upstream + test mocks.
- Callers (LocalHolger:99, UI data:270, fetch_many callers).
- (later, separate)
handle_http2_requestbody +ConnectorTrait::download_asset— not on the fetch hot path.
Build breaks: none expected (deref-compatible). One commit per phase; tests after each. Source: read-only scout 2026-06-28.