memory: bound cache admission and eviction - #2
Merged
Conversation
ravi-pplx
marked this pull request as ready for review
August 31, 2026 20:37
ravi-pplx
force-pushed
the
ravi/bound-cachew-memory-eviction
branch
from
September 1, 2026 04:38
2aa97c3 to
5a43c73
Compare
Replace the process-wide quadratic eviction path with sharded, fixed-work CLOCK admission. Revalidate planned victims with hit generations so competing planners cannot erase a recent reference. Account retained entries, incomplete writers, and reader-pinned generations against one configured ceiling. Grow writer buffers lazily and reserve only net capacity so declared lengths cannot allocate memory before body data and streaming chunk boundaries do not change admission. Reject invalid limit conversions and stop cancelled eviction plans before committing victims. Decline only the optional memory copy when bounded admission cannot obtain capacity, preserving authoritative-tier and client delivery. Cover replacement, cancellation, concurrent admission, tier fallback, accounting transitions, shutdown, and writer growth. Add cardinality and configured-path benchmarks that distinguish successful admissions from bounded declines. Verification: bin/just fmt; bin/just lint; bin/just test.
ravi-pplx
force-pushed
the
ravi/bound-cachew-memory-eviction
branch
from
September 1, 2026 05:12
5a43c73 to
cb06519
Compare
Cap declared-length buffers at their promised size, reject finite inflight limits that leave no retained capacity, and keep unknown-length starter allocations aligned with the per-entry accounting floor. Restore the io.WriterTo hit path and make Tiered.Create abort both completed and late writers on cancellation or backend failure, preventing reservation leaks and nil-writer returns. Record low-cardinality admission-decline reasons, clarify bounded CLOCK and capacity semantics, and cover the lifecycle and configuration edges under race. Verification: bin/just fmt; bin/just lint; bin/just test.
Distinguish inflight sub-limit exhaustion from hard-budget exhaustion so a declined writer only runs CLOCK trimming when eviction can actually make room. Cover unlimited retention while another writer fully occupies the inflight budget. Release Tiered create contexts after child writers close, and report the write-to-discard benchmark as latency and allocation work rather than physical copy throughput. Verification: bin/just fmt; bin/just lint; bin/just test; focused race tests repeated 20 times; go vet ./internal/cache.
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.
Problem
The old memory tier evicted at capacity by collecting and selection-sorting
every cached object while holding one process-wide write lock. Admission cost
grew quadratically with cache cardinality, so a miss burst could block unrelated
hits. Incomplete writers and reader-pinned generations also did not share one
enforceable memory budget.
Change
reader-pinned generations against one ceiling.
buffers beyond the declared length.
net buffer growth.
capacity.
run eviction when it can make the reservation succeed.
one backend fails; release successful writer contexts on close.
io.WriterToon memory hits so response writes avoid a 32 KiBintermediate buffer.
cachew.memory.admission_declines_totalwith a boundedreasonlabel.room. Authoritative-tier writes and client delivery continue.
Capacity semantics
limit-mbbounds Cachew's accounted memory, not process RSS. Accounted memoryincludes retained buffer capacity, estimated entry metadata, and retired buffers
still pinned by readers. Every retained entry and incomplete writer carries a
minimum 4 KiB charge, so a 1 GiB cache can hold at most roughly 262,000 objects
even when their payloads are smaller.
limit-mb = 0means unlimited retention.inflight-limit-mbdefaults to zero for compatibility. When both limits arefinite, it must be smaller than
limit-mband is a sub-limit rather than extracapacity. With
limit-mb = 32768andinflight-limit-mb = 1024, retained datais trimmed toward 31 GiB and retained plus incomplete-writer accounting cannot
exceed 32 GiB. An unlimited cache may still use a positive inflight limit to
bound incomplete writes independently. Inflight exhaustion declines that
optional writer without trimming retained objects, because eviction cannot free
inflight capacity.
Declared bodies grow lazily but stop exactly at their promised length.
Unknown-length bodies grow geometrically; spare capacity is charged rather than
hidden, avoiding another full-body copy at publication. Growing a writer reserves
only the net capacity increase before copying. The allocator can briefly retain
both buffers, so RSS may transiently exceed accounted memory by at most the old
buffer capacity.
Before and after
Measured on an Apple M4 Max (
darwin/arm64). The public comparison runs the sameCreate+ 16 KiBWrite+Closeoperation against pre-PR commit410ebdfandthe core bounded-eviction commit
cb06519; values are medians of three one-secondruns. Follow-up commits preserve the same sharding and CLOCK algorithm.
Final-head cardinality checks at
ec489cb:Create+Write+CloseThe restored 1 MiB memory-hit
WriteTopath improves from 4.95 us/op and33,448 B/op at
cb06519to about 0.24 us/op and 680 B/op atec489cb, about a20x speedup while removing the 32 KiB scratch allocation. This benchmark writes
to a discard sink and therefore reports latency and allocations, not synthetic
byte-copy throughput. Parallel hot Open/Close remains about 0.27 us/op.
Saturated parallel admission completes in about 0.51 us per attempt and accepts
about 58% of optional memory copies; bounded declines do not shed client
requests.
Correctness coverage
delete, reader-pinned retirement, and shutdown.
successful close releases the derived writer context.
tier.