fix: correct four latent defects in negotiation, caching, staging, and routing - #2
Open
ctxswitch wants to merge 1 commit into
Open
fix: correct four latent defects in negotiation, caching, staging, and routing#2ctxswitch wants to merge 1 commit into
ctxswitch wants to merge 1 commit into
Conversation
…d routing npm content negotiation discarded the full representation's specificity, so `Accept: application/json, application/*` served abbreviated metadata to a client that had asked for full. Compare both specificities, mirroring negotiate_python. cacheprog's local-hit path returned without recording the use, so merge_manifest retained those entries against a stale `last_seen`: the best-predicted actions were the first to age out and the first evicted at the entry cap. A wholly local build now pays a manifest read-modify-write at close. Reservation::drop tested `outstanding` before `state`, so a zero-length body -- which reserves nothing but still stages a file -- leaked its `.part`. RingState::ring rebuilt the continuum unconditionally after taking the write lock, so every concurrent caller rehashed the whole membership under a lock that blocks all routing, precisely when the cluster is degraded.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2 +/- ##
==========================================
+ Coverage 88.12% 88.56% +0.44%
==========================================
Files 35 35
Lines 7093 7174 +81
==========================================
+ Hits 6251 6354 +103
+ Misses 842 820 -22 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
3 tasks
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.
Summary
First of six stacked PRs from a full review of
src/. This one carries only thebugs; the later phases carry hot-path allocation, deletions, deduplication, config
robustness, and build/docs/test work.
Four defects, each with a regression test written first and confirmed failing:
negotiate_npmdiscarded the full branch's specificity, so
Accept: application/json, application/*scored abbreviated(1, 1.0)against full(2, 1.0)and returnedabbreviated to a client that had asked for full. It now compares both
specificities, matching
negotiate_python.The local-hit branch returned without
record_used, andmerge_manifestretainsstored entries against their old
last_seen, refreshing only entries present inused. A perfectly predicted action therefore expired atMANIFEST_MAX_AGE_SECONDSand was evicted first under
MANIFEST_MAX_ENTRIES..partfile.Reservation::droptestedoutstandingbeforestate.Content-Length: 0short-circuitsreserve(0)leaving
outstanding == 0, butstage_reservedstill callsmark_temporaryandcreates the file.
RingState::ringtook the write lock andrebuilt unconditionally; the read fast path checks
earliest_retrybut nothingre-checked after acquiring. Every concurrent request rebuilt in full (160 SHA-256
hashes per member) under a lock that blocks all routing, exactly when the cluster
is degraded.
Verification
make ciclean, includingclippy --all-targets -- -D warnings. Each regressiontest was confirmed to fail before its fix.
New:
wildcard_accept_ranges_never_outrank_a_named_representation(
tests/integration/package_proxies.rs) — table driven, seven npm cases and fivesymmetric python cases.
New:
locally_answered_gets_refresh_their_manifest_entry(
src/cacheprog/cacheprog_test.rs).New:
dropping_a_zero_length_stage_removes_its_temporary_file(
src/storage/local/artifact_files/tests.rs).New:
concurrent_readmission_rebuilds_the_continuum_once(
src/agent/discovery_test.rs) — bug 4 has no single-threaded signal, so thisraces 8 barrier-released callers and asserts one shared
Arc.make ciDocumentation and examples are accurate
New or changed behavior has test coverage
Operational impact
A warm build that answers every get locally now costs two more upstream requests at
close (a manifest GET and PUT) than it did before, because its
usedmap is nolonger empty and
finalizeno longer skips the write-back. That write-back is thefix: without it the manifest cannot carry a correctly predicted action forward.
warm_build_costs_one_manifest_get_plus_one_get_per_distinct_outputwas updatedfrom 4 to 6 requests with an explicit breakdown.
npm clients sending a wildcard alongside an explicit type will now receive full
metadata where they previously received abbreviated. No storage format, HTTP route,
configuration, or security boundary changes.
Review notes
Start at
src/proxy/mod.rs:245and compare againstnegotiate_python:181— the twoare now structurally identical apart from their media-type tables and their
application/*defaults (python defaults to json, npm to abbreviated). Thosedefaults differ deliberately; the new python cases pin them, because a later PR in
this stack merges the two negotiators and the shared parsing is where this bug lived.
The request-count change in
tests/integration/build_cache.rsis the one placereviewing should be deliberate rather than mechanical: it is a real cost increase,
accepted on purpose.
Deliberately excluded: everything from the other five phases — no
Durabilitythreading, no
ChannelStoreErrormerge, no allocation or deduplication cleanups.