Skip to content

Fix LRU RAM cache seen filter never engaging below 100% full#13234

Open
phongn wants to merge 1 commit into
apache:masterfrom
phongn:fix-lru-seen-filter-threshold
Open

Fix LRU RAM cache seen filter never engaging below 100% full#13234
phongn wants to merge 1 commit into
apache:masterfrom
phongn:fix-lru-seen-filter-threshold

Conversation

@phongn

@phongn phongn commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

The tiered seen filter for the (default) LRU RAM cache never engaged at the documented fill levels: an integer-division bug made it engage only when the cache was 100% full, so a scan could pollute a partly-full cache.

Root cause

proxy.config.cache.ram_cache.use_seen_filter greater than 1 is documented to enable the seen filter once the cache is more than n% full (2 = 50%, 3 = 67%, 4 = 75%, up to 9 = 90%). The check was:

(bytes >= max_bytes * (1 - (1 / cache_config_ram_cache_use_seen_filter)))

cache_config_ram_cache_use_seen_filter is an int, so 1 / N is integer division and is 0 for every N > 1. The expression collapses to bytes >= max_bytes, i.e. the filter engages only at 100% full, and values 2–9 all behave identically.

Fix

Rewrite the comparison so it is exact in integer arithmetic and overflow-safe at realistic cache sizes:

(bytes * cache_config_ram_cache_use_seen_filter >= max_bytes * (cache_config_ram_cache_use_seen_filter - 1))

N=2 → ≥50%, N=3 → ≥67%, N=9 → ≥89%, as documented. (Only the LRU RAM cache uses the tiered percentage; CLFUS treats the knob as on/off and is unaffected.)

Test

Adds ram_cache_lru_seen_filter: with use_seen_filter = 2, fill the cache to ~60% then Put 20 unseen keys once each — above the 50% threshold the filter must reject them. Verified it fails on the old form (20/20 admitted at 60% full) and passes on the fix (0/20). The existing ram_cache regression test still passes.

Impact

Operators who set use_seen_filter to 2–9 to get scan resistance once the cache is partly full currently get none until it is completely full. This affects the default RAM cache (LRU).

proxy.config.cache.ram_cache.use_seen_filter values above 1 are
documented to turn on the seen filter once the cache is (N-1)/N full
(2 = 50%, 3 = 67%, ... 9 = 90%). The threshold was written as
bytes >= max_bytes * (1 - (1 / N)) with N an int, so 1 / N is integer
division and evaluates to 0 for every N > 1; the test became
bytes >= max_bytes and the filter only engaged when completely full.
A scan could therefore pollute a half-full cache.

Rewrite the comparison as bytes * N >= max_bytes * (N - 1), which is
exact in integer arithmetic and overflow-safe at realistic cache
sizes. Add ram_cache_lru_seen_filter, which fails on the old form
(20/20 unseen keys admitted at 60% full) and passes on the fix (0/20).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes the LRU RAM cache “seen filter” engagement threshold so that proxy.config.cache.ram_cache.use_seen_filter values 2–9 activate the filter at the documented partial-fill levels (instead of only at 100% due to integer division). It also adds a regression test to ensure the filter engages once the cache is above the configured threshold.

Changes:

  • Replace the integer-division-based seen-filter threshold check with an equivalent integer-arithmetic comparison.
  • Add a nightly regression test validating that the tiered LRU seen filter begins rejecting single-seen inserts above the configured fill threshold.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/iocore/cache/RamCacheLRU.cc Fixes the seen-filter engagement condition for use_seen_filter > 1 to match documented behavior.
src/iocore/cache/CacheTest.cc Adds a nightly regression test to verify the LRU seen filter engages at the intended cache fill level.

Comment on lines 192 to +198
if ((cache_config_ram_cache_use_seen_filter == 1) ||
// If proxy.config.cache.ram_cache.use_seen_filter is > 1, and the cache is more than <n>% full, then use the seen filter.
// <n>% is calculated based on this setting, with 2 == 50%, 3 == 67%, 4 == 75%, up to 9 == 90%.
((cache_config_ram_cache_use_seen_filter > 1) && (bytes >= max_bytes * (1 - (1 / cache_config_ram_cache_use_seen_filter))))) {
// For use_seen_filter > 1, only apply the filter once the cache is more than <n>% full:
// 2 == 50%, 3 == 67%, 4 == 75%, up to 9 == 90%. Written as bytes * N >= max_bytes * (N - 1)
// rather than bytes >= max_bytes * (1 - 1 / N); the latter evaluates 1 / N in integer
// arithmetic (0 for every N > 1), so the filter only ever engaged at 100% full.
((cache_config_ram_cache_use_seen_filter > 1) &&
(bytes * cache_config_ram_cache_use_seen_filter >= max_bytes * (cache_config_ram_cache_use_seen_filter - 1)))) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will update the configuration parser for proxy.config.cache.ram_cache.use_seen_filter to only allow integer values 0-9 when using LRU in a separate PR, and also to only allow values 0-1 for CLFUS.

At the moment nobody should be using sufficiently large values to cause integer overflow.

@bryancall

Copy link
Copy Markdown
Contributor

@moonchen Can you determine how big of an issue this is and if we should backport it to 10.1.x or 10.2.x?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants