[STORE] Implement FIFO eviction for OffsetAllocatorStorageBackend#2880
Conversation
- Add OffsetAllocatorBackendConfig with env-var based tuning (policy, byte/key watermarks, eviction caps, node capacity). - Extend ObjectEntry with fifo_seq for global FIFO ordering. - Implement EvictToMakeRoom with dual-dimension (bytes/keys) watermarks, lazy-repair of stale fifo_index_ entries, and batch_keys protection. - Rewrite BatchOffload to notify master before allocate() and perform bounded fallback eviction using largest_free_region_ progress detection. - Update IsEnableOffloading to return true when eviction is enabled. - Add unit tests for FIFO order, key-count trigger, master notify timing, post-loop flush, NONE fallback, and concurrent read safety.
There was a problem hiding this comment.
Code Review
This pull request implements watermark-driven and fallback eviction policies (such as FIFO) for the OffsetAllocatorStorageBackend to manage storage capacity and key limits. The review feedback highlights two issues: a mathematically flawed progress check in the fallback eviction loop that prematurely terminates when evicting small keys for a larger allocation, and an incomplete warning condition when parsing the MOONCAKE_OFFSET_MAX_EVICT_PER_OFFLOAD environment variable that fails to warn on negative values.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
…loop - Read MAX_EVICT_PER_OFFLOAD as int64_t so negative values are caught and logged instead of silently wrapping to SIZE_MAX. - Tighten fallback eviction progress detection: continue while the allocator's largest_free_region_ grows, allowing small victims to coalesce into a region large enough for the incoming record.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces watermark-driven FIFO eviction capabilities to the OffsetAllocatorStorageBackend, including configuration validation, proactive and fallback eviction logic in BatchOffload, and comprehensive unit tests. A critical logical bug was identified in the fallback eviction loop where clearing the evicted_keys vector causes the loop to prematurely break on the first iteration. A fix has been suggested to track the number of evicted keys per iteration before clearing the vector.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
- Introduce evicted_this_turn to avoid recomputing and make the no-victims check consistent with the fallback budget accounting.
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
he-yufeng
left a comment
There was a problem hiding this comment.
Read through the eviction path end to end. This is a careful implementation, and a few things are done right that are easy to get wrong: the dual-dimension (bytes + keys) high/low watermarks, the batch_keys protection so in-flight keys are never evicted mid-write, the lazy-repair of stale fifo_index_ slots, and the consistent eviction_mutex_ -> shard.mutex lock order across both the insert and evict paths. I also checked that the total_value_size > UINT32_MAX guard from #2570 survived the BatchOffload rewrite (and you extended it with the record_size > UINT32_MAX check), and that eviction frees the extent through the RefCountedAllocationHandle refcount so a concurrent BatchLoad reader keeps its bytes pinned. Nice.
A few points, none blocking:
-
Single-writer invariant.
EvictToMakeRoomand the insert both do a check-then-act ontotal_size_/total_keys_, and the atomics only make each individual load/store atomic, not the sequence. You already DCHECK and note "singleheartbeat_thread_serialises offload; if concurrent offload is added, these must be re-evaluated." Since the whole watermark accounting rests on that, it would be worth making the assumption harder to break by accident, e.g. a short comment or DCHECK at theBatchOffloadentry stating that offload is single-writer, so a future change that parallelises offload doesn't silently turn the watermark math into a TOCTOU. -
fifo_index_orphan cleanup. The lazy-repair comment mentions "orphan slot from overwrite or prior deletion", but the overwrite path already erases the oldfifo_sequnder the lock, and I couldn't find a per-key delete path for this backend that removes fromshard.mapwithout also cleaningfifo_index_. Is the "prior deletion" case actually reachable today? If a delete/remove path exists (or is planned), it should erase thefifo_index_slot under the same lock order, otherwise orphans accumulate between eviction runs on a delete-heavy workload. If keys are only ever removed via evict/overwrite, the comment could just say so. -
Silent skip observability. The fallback loop bails on
now_largest <= prev_largest(fragmentation, or extents still pinned by readers) and skips the key with a one-shot warning. Under concurrent-read or fragmentation pressure this degrades offload quietly. A counter/metric for eviction-driven skips would let operators tell "watermark is doing its job" apart from "we're thrashing and dropping offloads".
Question on tests: does the concurrent-read-safety case assert the pinned-extent path, i.e. evict a key while a BatchLoad refcount is held and confirm the allocator does not re-issue that offset until the read completes? That is the load-bearing safety property here, so an explicit assertion would be good to have.
2a7a3ae to
6edf0c7
Compare
Thanks for the insightful suggestions — all of the points you raised have been addressed in the latest commit. |
- Document single-writer precondition in BatchOffload - Clarify fifo_index_ orphan cleanup defense in EvictToMakeRoom - Add eviction_skips_ counter with GetEvictionSkips() accessor - Strengthen OffsetAllocatorStorageBackend_Eviction_ConcurrentReadSafety test to assert refcount prevents re-issuing in-use extents
6edf0c7 to
6b3e519
Compare
he-yufeng
left a comment
There was a problem hiding this comment.
Thanks — went through 6b3e5199 and all four are addressed cleanly:
- The
SINGLE-WRITER PRECONDITIONblock inBatchOffloadnow spells out that the watermark check → evict → update sequence isn't atomic across threads and points at theDCHECK_GEguards, so a future concurrent-offload change can't silently turn the accounting into a TOCTOU. That's exactly what I was after. eviction_skips_with theGetEvictionSkips()accessor gives operators the signal to tell "watermark is doing its job" apart from "we're thrashing and dropping offloads".- The
fifo_index_orphan case is now documented inEvictToMakeRoom, so the reachability question is answered inline. - The strengthened
ConcurrentReadSafetytest asserts the load-bearing property directly: the refcount keeps an evicted key's extent pinned, the allocator doesn't re-issue an in-use offset, and there's zero corruption across the concurrent reads.
Nice work on the eviction path overall — the dual-dimension watermarks, in-flight batch_keys protection, and the consistent lock order make it a careful implementation. LGTM.
Description
Add FIFO eviction support to
OffsetAllocatorStorageBackend, matching the watermark-based eviction pattern already used byBucketStorageBackend. Previously, when the offset allocator ran out of space, offload was permanently disabled (KEYS_ULTRA_LIMIT). With this change, the backend can now evict old keys (FIFO order) to make room for new ones, and notify the master via the existingeviction_handlercallback.Key points:
eviction_policy=NONEpreserves the original break-on-full path byte-for-byte.AllocationPtr(shared_ptr) keeps in-flight read extents pinned, so eviction is a simple shard-map erase — no two-phase delete or inflight-reads spin-wait needed (unlike Bucket).allocate()fails despite watermarks being satisfied (fragmentation / node exhaustion), a bounded retry loop evicts victims and recheckslargest_free_region_.FileStorageorMasterService: theeviction_handler → BatchEvictDiskReplicacontract is already in place.Module
mooncake-store)Type of Change
How Has This Been Tested?
Test commands:
Test results:
New eviction tests:
Eviction_FifoOrderEviction_NoEvictionWhenNONEEviction_MasterNotifiedBeforeReuseEviction_PostLoopFlushEviction_KeyCountTriggerEviction_ConcurrentReadSafetyChecklist
./scripts/code_format.shpre-commit run --all-filesand all hooks passdocs/source/deployment/offset-evict.mdAI Assistance Disclosure
AI tools (Claude) assisted with design exploration, adversarial review, and iterative bug-fixing of the eviction implementation. The human submitter reviewed and approved all changes.