Skip to content

Harden the compressed GrpcStore download path - #2640

Merged
palfrey merged 5 commits into
TraceMachina:mainfrom
Reactor-Inc:user/wgray/grpc-wire-compression-fixes-pr
Aug 5, 2026
Merged

Harden the compressed GrpcStore download path#2640
palfrey merged 5 commits into
TraceMachina:mainfrom
Reactor-Inc:user/wgray/grpc-wire-compression-fixes-pr

Conversation

@walter-zeromatter

@walter-zeromatter walter-zeromatter commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Description

Follow-ups to the opt-in zstd wire compression added to GrpcStore in #2596.
These are correctness fixes in the compressed download path, plus one guard
on the compressed upload path, found while reviewing that work.

1. Pump-stage failures were misclassified as retryable.
get_part_compressed classified pump-stage failures with is_retryable_code,
but pump errors come from our own writer, and buf_channel reports a dropped
receiver as Internal — which counts as retryable. A consumer that hung up
mid-download therefore returned Ok(Some(forwarded)), and get_part responded
by issuing a second, full-size identity Read, streaming the remainder of the
blob into a channel nobody was reading. Pump failures are now always terminal:
they mean either the consumer went away or the decoder aborted, and the
decoder's own verdict is already surfaced by the arm above it, since try_join!
polls decode first.

2. Resume could fire after the download had already succeeded.
The fallback treated a late failure from another stage as a reason to resume
even when the blob had already been fully delivered and verified. The pump now
records that it forwarded the decoder's EOF — which the decoder only emits after
the blob passes its size and digest checks — and a late error after that point
resolves as success instead of resuming into an already-closed writer.

3. A genuine resume over-requested.
The identity retry now asks for exactly the undelivered tail. It previously
reused the caller's original length, which the entry guard only permits when
it covers the whole blob, so the resumed request ran past the end of the blob
and relied on the server clamping it.

4. Compressed upload is now restricted to real digest keys.
A string key's into_digest() hashes the key itself, which the remote's
mandatory digest verification would reject. Also skips the background drain
spawn when the encoder finished cleanly and the reader is already at EOF.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

New cases in nativelink-service/tests/grpc_wire_compression_test.rs covering
the aborted-consumer path and the resume path. The resume path had no coverage
at all before this change:

  • The fake ByteStream now honors read_offset / read_limit, so the resumed
    range can be asserted byte-exactly rather than just "it eventually finished".
  • Added a mid-frame abort mode and a mixed-entropy payload helper, since
    make_content compresses far enough that multi-megabyte blobs otherwise fit
    in a single wire chunk and never exercise the partial paths.
  • Resume is asserted both with length: None and with an explicit length
    covering the blob. Only the latter over-requested before this change, because
    read_limit: 0 already means "to the end" on the wire.

Verified locally on this branch:

bazel test //...
# Executed 73 out of 102 tests: 102 tests pass.
# (rustfmt + clippy aspects are enabled by default in .bazelrc, so lint runs with the build)

Checklist

  • Updated documentation if needed (no user-facing config or behavior change)
  • Tests added/amended
  • bazel test //... passes locally (ran the store + service wire-compression
    targets; the aspects cover clippy/rustfmt for the touched crates)
  • PR is contained in a single commit

This change is Reviewable

Follow-ups to the wire-compression work added in TraceMachina#2596.

`get_part_compressed` classified pump-stage failures with
`is_retryable_code`, but pump errors come from our own writer and
`buf_channel` reports a dropped receiver as `Internal`, which counts
as retryable. A consumer that hung up mid-download therefore returned
`Ok(Some(forwarded))` and made `get_part` issue a second, full-size
identity Read streaming the remainder into a channel nobody was
reading. Pump failures are now always terminal: they mean either the
consumer went away or the decoder aborted, and the decoder's verdict
is already reported by the arm above it since `try_join!` polls
decode first.

The fallback also treated a late failure from another stage as a
reason to resume even when the download had already completed and
been verified. The pump now records that it forwarded the decoder's
EOF -- which the decoder only sends after the blob passes its size
and digest checks -- and a late error after that point resolves as
success instead of resuming into an already-closed writer.

On a genuine resume, the identity request now asks for exactly the
undelivered tail. It previously kept the caller's original `length`,
which the entry guard only permits when it covers the whole blob, so
the resumed request ran past the end of the blob and relied on the
server clamping it.

The compressed upload path is now taken only for real digest keys: a
string key's `into_digest()` hashes the key itself, which the
remote's mandatory digest verification would reject. The background
drain spawn is also skipped when the encoder finished cleanly and the
reader is already at EOF.

Tests: new cases in the service suite for the aborted-consumer and
resume paths. The resume path had no coverage at all -- the fake
ByteStream now honors `read_offset`/`read_limit` so the resumed range
can be asserted byte-exactly, and grew a mid-frame abort mode plus a
mixed-entropy payload helper, since `make_content` compresses far
enough that multi-megabyte blobs fit in one wire chunk. Resume is
asserted both with `length: None` and with an explicit `length`
covering the blob; only the latter over-requested before this change,
since `read_limit: 0` already means "to the end" on the wire.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nativelink Ready Ready Preview Aug 5, 2026 11:08am
nativelink-aidm Ready Ready Preview Aug 5, 2026 11:08am

Request Review

@palfrey

palfrey commented Aug 3, 2026

Copy link
Copy Markdown
Member

Seeing a failure in corrupt_compressed_download_is_terminal and I'm not quite sure if this is a flaky test, or something with this PR (or both)? @walter-zeromatter Any thoughts there?

@walter-zeromatter

Copy link
Copy Markdown
Contributor Author

Looks like there was a nondeterministic test. Found & fixed, along with another unrelated bit of hardening

@palfrey
palfrey merged commit aba6718 into TraceMachina:main Aug 5, 2026
42 checks passed
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.

2 participants