Skip to content

perf(remote-cache): scan one level into a tar member that is itself a tar - #360

Open
raphaelvigee wants to merge 1 commit into
masterfrom
raphaelvigee/scan-tar-recurse-nested
Open

perf(remote-cache): scan one level into a tar member that is itself a tar#360
raphaelvigee wants to merge 1 commit into
masterfrom
raphaelvigee/scan-tar-recurse-nested

Conversation

@raphaelvigee

Copy link
Copy Markdown
Member

Follow-up to #356.

The waste

The content scan judges an artifact by its members' heads. That answers nothing for the shape where the artifact wraps another tar — an OCI bundle, a collected archive, a .tar a rule produced. The member's head is a tar header, no signature matches, the whole blob is charged as compressible, and it gets gzipped anyway — the exact waste #356 was added to stop, one level out of reach.

The change

Contained: a member already past the size floor is now also checked for the ustar magic at offset 257 — the only reason the peek widens from 12 bytes to 265 — and, when it is a tar, walked with the same signature table into the same tally. One level, no deeper; a tar inside a tar inside a tar is not a shape worth the seeks. The extra copy stays where it is.

Two things worth a reviewer's attention:

The nested walk seeks. tar skips by reading when its reader can't seek, so handing it the member as a plain Read would pull the entire member through just to reach the next header — the one thing this scan is built not to do. Window gives it a seekable view of the member's bytes instead. That is also why the descent happens after the outer walk rather than inside it: the iterator borrows the reader for as long as it lives, so nested members are collected as (offset, size) and visited once the reader is back.

The early exit survives. Whatever a nested walk neither credited nor rejected is charged back against the member's size before the next member is opened, so an archive of archives that is not precompressed gives up as promptly as an archive of text. A deferred member does escape the outer walk's early exit — its bytes are genuinely unknown until walked, and charging them beforehand would give up on precisely the archives this recursion exists to catch. Only members past MIN_MEMBER_BYTES are ever deferred, so the tiny-member case that exit was built for cannot reach this path at all.

Correctness

Verdict semantics are unchanged, and it still never feeds a hash — the same artifact compressed or not is the same artifact. Every new answer is in the safe direction on failure: a v7 tar (no magic) is not recursed into, a member that merely looks like a tar header fails to parse and is charged in full, and a nested walk that errors leaves the member charged in full. Each costs a wasted gzip at worst, which is the status quo.

Tests

Six new, all in crates/core/src/hartifactcontent/sniff.rs:

  • the wrapping shape is now recognized (nested gzip → precompressed, dominant: gzip)
  • a nested tar of text still compresses
  • the recursion stops at one level (triple-nested gzip is not credited)
  • a member that only looks like a tar is charged in full, not forgiven
  • the nested walk hits the early exit — two 1000-member nested archives settle within ~10% of the blob, and the second is never opened
  • all pre-existing scan behaviour, including the original early-exit cost guard, unchanged

lint clean, cargo test -p core --lib green (145 passed).

🤖 Generated with Claude Code

https://claude.ai/code/session_01UnRHitwvHJFjDAeKef5Uwb

… tar

The content scan judges an artifact by its members' heads, which answers
nothing for the shape where the artifact *wraps* another tar — an OCI
bundle, a collected archive, a `.tar` a rule produced. The member's head
is a tar header, no signature matches, and the whole blob is charged as
compressible and gzipped anyway, which is exactly the waste the scan was
added to stop.

Follow one level. A member already past the size floor is now also
checked for the `ustar` magic at offset 257 — the only reason the peek
widens from 12 bytes to 265 — and, when it is a tar, walked with the same
signature table and the same tally. No deeper.

The nested walk seeks. `tar` skips by *reading* when its reader cannot
seek, so handing it the member as a plain `Read` would pull the whole
member through just to reach the next header; `Window` gives it a
seekable view of the member's bytes instead, which is also why the
descent happens after the outer walk rather than inside it (the iterator
borrows the reader for as long as it lives).

The early exit survives: whatever a nested walk neither credited nor
rejected is charged back against the member's size before the next member
is opened, so an archive of archives that is not precompressed gives up
as promptly as an archive of text. A deferred member does escape the
*outer* walk's early exit — its bytes are unknown until walked — but only
members past MIN_MEMBER_BYTES are ever deferred, so the tiny-member case
that exit was built for cannot reach the path at all.

Verdict semantics are unchanged and it still never feeds a hash: the same
artifact compressed or not is the same artifact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UnRHitwvHJFjDAeKef5Uwb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant