fix(dig-node): sweep modules-cache after reshare-warm promote_into_cache land (#2053) - #172
Merged
Merged
Conversation
Co-Authored-By: Claude <noreply@anthropic.com>
…che land (#2053) The read-triggered reshare-warm whole-capsule promotion (CapsuleWarmer::warm -> promote_into_cache) landed a whole .dig into <cache>/modules without running the tier-aware size-cap sweep, so the disk bound held only while the tier-0 precache loop (#1934) or a read-path sync (#2041) happened to run. This closes the last unbounded on-demand land path: warm() now runs the sweep after a SUCCESSFUL (Held) land only. Lock context: a reshare warm holds NO cache_lock (the warmer is a standalone seam with no Node guard across the land), so it drives the async Node::evict_modules_if_needed (takes cache_lock fresh), never the locked core. The sweep reuses the SAME ModulesCacheEvictor seam the tier-0 loop uses, so both paths bound the cache through one impl. A reshare-warm module is untagged -> module_tier returns the protected Tier1Demand default, so the sweep sacrifices Tier0Precache inventory first and the just-warmed module survives its own sweep. Co-Authored-By: Claude <noreply@anthropic.com>
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.
Closes dig_ecosystem#2053. Completes the "every on-demand land path sweeps"
<cache>/modulesdisk-bound invariant (the #1934 / #2041 arc) — the reshare-warm land was the last land site without a tier-aware size-cap sweep.Why
Surfaced by the #2041 loop-security audit:
promote_into_cache(crates/dig-node-core/src/seams/dig_peer/module_reshare.rs) lands a whole capsule into<cache>/modules(atomic write-then-rename) with NO subsequent size-cap sweep, unlikecache_fetch_and_cache(#1934) and the read-path §21 sync (#2041). A remotely-triggered read fires the reshare warm (reader → holder, #1576), so repeated warms could grow the modules cache past the cap with the tier-0 loop idle — the same unbounded-growth class #2041 closed on the read path, still open on this one. Self-driven (the reshare flywheel), so lower severity than the read-path residual, but it left the SPEC's "every on-demand land" claim untrue.Lock-context audit (the load-bearing step)
The reshare-warm land runs in
CapsuleWarmer::warm()— a standalone seam reached viaspawn_capsule_warm→ a fire-and-forgettokio::spawn. It holds nocache_lock(no Node handle, no guard across the land). So the sweep uses the asyncNode::evict_modules_if_needed().await(takescache_lockfresh) — mirroring #2041'ssync_module_and_bound. The locked coreevict_modules_locked()would evict without serialization here; the async variant under a held lock would deadlock — neither hazard applies since no lock is held. Injected via the existingtier0_live::ModulesCacheEvictorseam (the sameNodeModulesEvictorthe tier-0 loop uses) — no new, driftable evictor.Tier: a reshare-warm module is untagged (never
mark_tier0_land), somodule_tierreturns the protectedTier1Demanddefault (#2015 fail-safe). The sweep sacrificesTier0Precacheinventory first and the just-warmed module survives its own sweep — treated as its real (demand) tier, neither wrongly protected nor wrongly sacrificed. No tier-tag change needed.What changed
module_reshare.rs—CapsuleWarmergains anevictorseam;warm()runs the sweep after a successfulHeldland.tier0_live.rs—ModulesCacheEvictor/NodeModulesEvictorexposedpub(crate)+NodeModulesEvictor::new; test-onlyNoopModulesEvictor.download.rs/peer.rs— wireNodeModulesEvictor::new(node)throughwire_capsule_reshare.SPEC.md §3.4— reshare-warm added to the on-demand land paths + the untagged→Tier1Demand note.CapsuleWarmer::new+wire_capsule_resharepub→pub(crate)(both crate-internal only — verified no external consumer; required to inject the crate-private evictor without aprivate_interfacesclippy error).How verified (TDD)
reshare_warm_land_bounds_modules_cache_evicting_the_tier0_victim(lib.rs) — real Node + realNodeModulesEvictor+ mock transport: a real warm lands module A past the cap, the pre-seeded oversized tier-0 victim B is evicted, A survives.a_successful_reshare_warm_land_sweeps_the_modules_cache_once(module_reshare.rs, spy) — exactly one sweep onHeld.a_refused_reshare_warm_does_not_sweep_the_modules_cache(module_reshare.rs, negative control) — zero sweeps on refusal.cargo test -p dig-node-core --lib→ 656 passed, 0 failed; fmt +clippy -D warningsclean; build OK.Version
root
[workspace.package].version0.93.3 → 0.93.4 (patch,fix:);Cargo.lockregenerated.Generated by Claude Code