Repository navigation
fix: reject short cache range reads - #251
Open
perfloop[bot] wants to merge 6 commits into
Open
perfloop[bot] wants to merge 6 commits into
perfloop[bot] wants to merge 6 commits into
Conversation
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Short positive cache range reads are rejected as incomplete instead of accepted as successful cache reads. After a block response commits, TAG can recover the unwritten suffix from an upstream range only when the cached ETag and exact range match. Changed-ETag bytes are rejected, and client write failures are not retried.
The guard applies to block and whole-object cache range reads. Cache-bypass and upstream-miss paths are unchanged; no new setting or S3 route is added.
X-Cache: HITidentifies a response selected through a cache entry when headers commit. A block stream may include a verified upstream suffix, and this header does not guarantee per-byte cache provenance or transfer completion. Cache-hit counters follow that committed status; request success/error metrics record completion separately. The metrics guide'ssource=localshare describes the request-handling path, not upstream avoidance or per-byte origin.If a changed ETag is seen after a full block response commits, TAG does not append the new version's suffix. The handler reports an error, but HTTP 200 and the original Content-Length remain; the client may receive only the cached prefix. A whole-object cached Range GET commits its 206 after the first byte and cannot restart upstream; a later short read returns an error while its 206 and Content-Length remain. The README and S3/cache-control documentation describe these boundaries.
The regression uses the repository-pinned ocache v1.13.0 in-memory client with stored bytes shorter than metadata, plus an
httptest.NewServerand the real forwarder for suffix responses. It covers same-ETag recovery, changed-ETag rejection, whole-object Range short reads, and client-write failures. This fixture shows a possible backend short read, not that normal population creates short entries or how often deployed traffic sees one.The correctness assertion is satisfied on the final source and violated on
4df832ad38e8d9cc6cea919603be1ccd7657b75f. The standalone claim runner is retained proof support and is not part of this pull request.The block-mode S3 compatibility suite was not run here.
make s3-test-local-blocksbuilt TAG but stopped before startup because this runtime has no AWS credentials for Tigris, somake s3-testsdid not run. Run those commands in a credentialed environment before merge.Local validation passed:
make lint-ci,env TAR_OPTIONS=--no-same-owner make test,env TAR_OPTIONS=--no-same-owner make test-integration,env TAR_OPTIONS=--no-same-owner make test-race, andenv TAR_OPTIONS=--no-same-owner make test-coverage. These checks used Go 1.27.1; the GitHub workflow uses Go 1.24.2. Full repository CI remains pending.Generated by Perfloop.
Note
Medium Risk
Changes GetObject streaming, block degraded-serve recovery, and request metrics on the cache serve path; committed responses may still be partial on error, but behavior is now explicit and tested.
Overview
Cached range reads must deliver the full requested byte span.
getRangeStreamByKeynow errors on short or over-long reads from ocache (after writing any partial bytes), so a “successful” cache read cannot silently truncate a Range or block-local slice.Proxy behavior and observability follow that contract. Block-mode serves can still finish a committed
X-Cache: HITby fetching a same-ETag upstream suffix; changed-ETag remainders are rejected. Whole-object cached Range GETs stop on client write failures without masking them as success.RecordRequestResultrecords request success/error separately from committed cache hits; docs clarify thatHITandtag_cache_hits_totalreflect the cache decision at header commit, not per-byte origin or transfer completion.Regression coverage adds integration tests for suffix recovery, ETag mismatch, short whole-body ranges, and post-commit write failures.
Reviewed by Cursor Bugbot for commit af4effd. Bugbot is set up for automated code reviews on this repo. Configure here.