jplevyak opened a new pull request, #13893:
URL: https://github.com/apache/trafficserver/pull/13893

    ## Description
   
     ### Background & Problem
   
   The Clocked Least Frequently Used by Size (CLFUS) RAM cache replacement 
policy is designed to maximize request hit rates by balancing frequency, 
recency, object size density, and scan resistance. In production and sustained 
benchmarks, however, CLFUS degrades over time and eventually underperforms a 
simple LRU cache.
   
   While S3-FIFO is a generally good caching algorithm, CLFUS is sensitive to 
object sizes which may give it some advantages on particular mixed size 
workloads, e.g. large streaming objects combined with small metadata objects 
and additionally it support compression which might help on certain systems.  
Your mileage may vary.
   
    Several algorithmic bugs and design flaws causing this degradation:
   
     1. No frequency decay for spared resident entries ("Cache Squatters"):
     When an entry at the head of _lru[0] is evaluated for eviction and spared 
because its value density exceeds _average_value, its hits counter was never 
decayed or reset (contrary to the CLOCK mechanism described in the 
documentation). Past popular entries retained massive hit counts indefinitely, 
becoming permanent "zombies" that could never be evicted.
     2. Recency inversion on cache hits:
     In RamCacheCLFUS::get(), an entry in memory was only promoted to the tail 
of _lru[0] if cache_value(e) > _average_value. Active, moderately sized objects 
(e.g., 30 KB assets) with below-average value density were never refreshed on 
hits and were prematurely evicted from the front of the queue while actively 
serving client traffic.
     3. Redundant 3-miss admission barrier & premature history expiration:
     Objects had to clear both the seen hash table and the history queue 
(_lru[1]) before being cached in memory, requiring ≥ 3 requests. Concurrently, 
_tick() halved history entries immediately (1 >> 1 == 0), discarding new 
history entries on their very first tick, while get() ignored accesses to 
history entries without refreshing them.
     4. Extreme size penalty:
     Linear size division (hits + 1)/(size + 256) imposed an insurmountable 
penalty (e.g., 64 × to 200 ×) on standard web objects (10 KB–200 KB) compared 
to sub-kilobyte files.
     5. Requeue loop churn:
     When _average_value was uninitialized or low, requeue_limit = 100 allowed 
eviction passes to spin multiple times through small caches in a single put().
     ──────
     ### Solution
   
     This PR implements five targeted improvements:
   
     1. Clock hit decay on spared victims (RamCacheCLFUS.cc):
     When an entry is spared during eviction, its hit count is halved 
(victim->hits >>= 1;), bounding unreferenced item lifetime to O(log(hits)) 
clock passes. Additionally, requeue_count is bounded by std::min(requeue_limit, 
this->_objects) to prevent multiple loops over the same items in a single put.
     2. Unconditional recency promotion on hit (RamCacheCLFUS.cc):
     Removed the cache_value(e) > _average_value requirement in get(). Any hit 
on a resident object unconditionally moves it to _lru[0].tail and increments 
e->hits.
     3. Streamlined admission & history preservation (RamCacheCLFUS.cc):
         • Keys matching the seen filter on their second sighting are permitted 
to compete for direct admission into _lru[0] rather than being forced to 
Lhistory.
         • Lookups hitting history entries in get() increment e->hits and 
refresh the entry to _lru[1].tail so it survives until the disk fetch completes.
         • New history entries are initialized with hits = 2 to grant a 
multi-tick second chance.
     4. Dampened size penalty (RamCacheCLFUS.h):
     Replaced linear size division with an effective size curve: eff_size = 
(size >> 1) + 2048. This preserves strict size monotonicity while dampening the 
penalty ratio between 1 KB and 64 KB objects to < 15 ×.
     5. Periodic global frequency aging (RamCacheCLFUS.cc):
     Added _decay_epoch() running on a 30-second epoch under the stripe lock to 
halve hit counts and decay _average_value, preventing obsolete popularity peaks 
from skewing admission during working set transitions.
     ──────
     ### Tests Added & Verified
   
     Enhanced test_RamCacheCLFUS.cc with 5 dedicated Catch2 test cases:
   
     • CLFUS decays hit count when sparing victims during eviction
     • CLFUS unconditionally promotes resident entries to the tail on hits
     • CLFUS history entry is refreshed on get() hit
     • CLFUS size dampening curve bounds disparity between small and large 
objects
     • CLFUS periodic epoch decay halves hit counts and decays average value
   
     #### Verification:
   
     • ctest -R RamCache passed 100% (23/23 tests, including RamCacheCLFUS, 
RamCacheHitRate, RamCacheSeenFilter, RamCacheCopy, and RamCacheCompressEntries).
     • Formatted with cmake --build build -t format.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to