Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 59 additions & 4 deletions doc/admin-guide/files/records.yaml.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2892,10 +2892,65 @@ RAM Cache

.. ts:cv:: CONFIG proxy.config.cache.ram_cache.algorithm INT 1

Two distinct RAM caches are supported, the default (1) being the simpler
**LRU** (*Least Recently Used*) cache. As an alternative, the **CLFUS**
(*Clocked Least Frequently Used by Size*) is also available, by changing this
configuration to 0.
Three RAM cache eviction algorithms are supported, selected by this value:

``1``
**LRU** (*Least Recently Used*), the default -- the simplest policy,
favoring recency. Pairs with
:ts:cv:`proxy.config.cache.ram_cache.use_seen_filter` for scan
resistance.

``0``
**CLFUS** (*Clocked Least Frequently Used by Size*), which balances
recency, frequency, and object size. It is the only algorithm that
supports in-RAM compression
(:ts:cv:`proxy.config.cache.ram_cache.compress`).

``2``
**S3-FIFO** (*Simple Scalable Static FIFO*): a small admission queue and
a main queue (both FIFO), plus a ghost queue of recently evicted keys,
which together filter one-hit-wonders. Scan-resistant and inexpensive
(no per-hit reordering); strong hit rates on CDN and key-value
workloads. Its eviction metadata (including the ghost) is accounted
within :ts:cv:`proxy.config.cache.ram_cache.size`. Experimental; it does
not use the seen filter or support in-RAM compression. Its queue split,
ghost bounds, and promotion threshold can be tuned with the
``proxy.config.cache.ram_cache.s3fifo.*`` settings below; the defaults
follow the original paper and suit most workloads.

.. ts:cv:: CONFIG proxy.config.cache.ram_cache.s3fifo.main_percent INT 90

Only applies when :ts:cv:`proxy.config.cache.ram_cache.algorithm` is ``2``
(S3-FIFO). The target size of the main queue as a percentage of the resident
budget; the remainder is the small admission queue. The default ``90`` gives
the ~10% small / ~90% main split from the paper. Valid range is ``1`` to
``99`` (an out-of-range value is rejected with a warning and the default is
used); a larger value grows the main queue at the expense of the admission
queue.

.. ts:cv:: CONFIG proxy.config.cache.ram_cache.s3fifo.ghost_size_percent INT 90

Only applies when :ts:cv:`proxy.config.cache.ram_cache.algorithm` is ``2``
(S3-FIFO). The ghost queue remembers the keys of recently evicted objects for
up to this percentage of :ts:cv:`proxy.config.cache.ram_cache.size` worth of
object bytes. Valid range is ``0`` to ``100``; ``0`` disables this bound.

.. ts:cv:: CONFIG proxy.config.cache.ram_cache.s3fifo.ghost_mem_percent INT 25

Only applies when :ts:cv:`proxy.config.cache.ram_cache.algorithm` is ``2``
(S3-FIFO). Caps the memory the ghost queue's per-key metadata may consume at
this percentage of :ts:cv:`proxy.config.cache.ram_cache.size`. This metadata
is counted against the configured RAM cache size, so total memory stays
within the budget regardless of object cardinality. Valid range is ``0`` to
``100``; ``0`` disables the ghost queue.

.. ts:cv:: CONFIG proxy.config.cache.ram_cache.s3fifo.promote_threshold INT 2

Only applies when :ts:cv:`proxy.config.cache.ram_cache.algorithm` is ``2``
(S3-FIFO). The number of times an object in the small admission queue must be
reused before it is promoted to the main queue instead of being demoted to
the ghost. The default ``2`` admits objects seen at least twice. Valid range
is ``1`` to ``3``; a higher value makes admission to the main queue stricter.

.. ts:cv:: CONFIG proxy.config.cache.ram_cache.use_seen_filter INT 1

Expand Down
14 changes: 9 additions & 5 deletions doc/admin-guide/storage/index.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,22 @@ and reduces load on disks, especially during temporary traffic peaks.
You can configure the RAM cache size to suit your needs, as described in
:ref:`changing-the-size-of-the-ram-cache` below.

The RAM cache supports two cache eviction algorithms, a regular *LRU*
(Least Recently Used) and the more advanced *CLFUS* (Clocked Least
The RAM cache supports three cache eviction algorithms: a regular *LRU*
(Least Recently Used); the more advanced *CLFUS* (Clocked Least
Frequently Used by Size; which balances recentness, frequency, and size
to maximize hit rate, similar to a most frequently used algorithm).
The default is to use *LRU*, and this is controlled via
to maximize hit rate, similar to a most frequently used algorithm); and
*S3-FIFO* (Simple Scalable Static FIFO), a FIFO-based policy whose small
admission queue and ghost list filter one-hit-wonders, giving strong hit
rates on CDN and key-value workloads at low cost. The default is to use
*LRU*, and this is controlled via
:ts:cv:`proxy.config.cache.ram_cache.algorithm`.

Both the *LRU* and *CLFUS* RAM caches support a configuration to increase
scan resistance. In a typical *LRU*, if you request all possible objects in
sequence, you will effectively churn the cache on every request. The option
:ts:cv:`proxy.config.cache.ram_cache.use_seen_filter` can be set to add some
resistance against this problem.
resistance against this problem. *S3-FIFO* is scan-resistant by design,
through its admission queue, and does not use the seen filter.

In addition, *CLFUS* also supports compressing in the RAM cache itself.
This can be useful for content which is not compressed by itself (e.g.
Expand Down
5 changes: 3 additions & 2 deletions include/iocore/cache/Cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ static constexpr ts::ModuleVersion CACHE_MODULE_VERSION(1, 0);

#define SCAN_KB_PER_SECOND 8192 // 1TB/8MB = 131072 = 36 HOURS to scan a TB

#define RAM_CACHE_ALGORITHM_CLFUS 0
#define RAM_CACHE_ALGORITHM_LRU 1
#define RAM_CACHE_ALGORITHM_CLFUS 0
#define RAM_CACHE_ALGORITHM_LRU 1
#define RAM_CACHE_ALGORITHM_S3FIFO 2

#define CACHE_COMPRESSION_NONE 0
#define CACHE_COMPRESSION_FASTLZ 1
Expand Down
1 change: 1 addition & 0 deletions include/iocore/eventsystem/Thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ class Thread
ProxyAllocator openDirEntryAllocator;
ProxyAllocator ramCacheCLFUSEntryAllocator;
ProxyAllocator ramCacheLRUEntryAllocator;
ProxyAllocator ramCacheS3FIFOEntryAllocator;
ProxyAllocator evacuationBlockAllocator;
ProxyAllocator ioDataAllocator;
ProxyAllocator ioAllocator;
Expand Down
1 change: 1 addition & 0 deletions src/iocore/cache/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ add_library(
PreservationTable.cc
RamCacheCLFUS.cc
RamCacheLRU.cc
RamCacheS3FIFO.cc
Store.cc
Stripe.cc
StripeSM.cc
Expand Down
70 changes: 41 additions & 29 deletions src/iocore/cache/Cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,35 +56,39 @@ constexpr ts::VersionNumber CACHE_DB_VERSION(CACHE_DB_MAJOR_VERSION, CACHE_DB_MI

// Configuration

int64_t cache_config_ram_cache_size = AUTO_SIZE_RAM_CACHE;
int cache_config_ram_cache_algorithm = 1;
int cache_config_ram_cache_compress = 0;
int cache_config_ram_cache_compress_percent = 90;
int cache_config_ram_cache_use_seen_filter = 1;
int cache_config_http_max_alts = 3;
int cache_config_log_alternate_eviction = 0;
int cache_config_dir_sync_frequency = 60;
int cache_config_dir_sync_delay = 500;
int cache_config_dir_sync_max_write = (2 * 1024 * 1024);
int cache_config_dir_sync_parallel_tasks = 1;
int cache_config_permit_pinning = 0;
int cache_config_select_alternate = 1;
int cache_config_max_doc_size = 0;
int cache_config_min_average_object_size = ESTIMATED_OBJECT_SIZE;
int64_t cache_config_ram_cache_cutoff = AGG_SIZE;
int cache_config_max_disk_errors = 5;
int cache_config_hit_evacuate_percent = 10;
int cache_config_hit_evacuate_size_limit = 0;
int cache_config_force_sector_size = 0;
int cache_config_target_fragment_size = DEFAULT_TARGET_FRAGMENT_SIZE;
int cache_config_agg_write_backlog = AGG_SIZE * 2;
int cache_config_enable_checksum = 0;
int cache_config_alt_rewrite_max_size = 4096;
int cache_config_read_while_writer = 0;
int cache_config_mutex_retry_delay = 2;
int cache_read_while_writer_retry_delay = 50;
int cache_config_read_while_writer_max_retries = 10;
int cache_config_persist_bad_disks = false;
int64_t cache_config_ram_cache_size = AUTO_SIZE_RAM_CACHE;
int cache_config_ram_cache_algorithm = 1;
int cache_config_ram_cache_compress = 0;
int cache_config_ram_cache_compress_percent = 90;
int cache_config_ram_cache_use_seen_filter = 1;
int cache_config_ram_cache_s3fifo_main_percent = 90;
int cache_config_ram_cache_s3fifo_ghost_size_percent = 90;
int cache_config_ram_cache_s3fifo_ghost_mem_percent = 25;
int cache_config_ram_cache_s3fifo_promote_threshold = 2;
int cache_config_http_max_alts = 3;
int cache_config_log_alternate_eviction = 0;
int cache_config_dir_sync_frequency = 60;
int cache_config_dir_sync_delay = 500;
int cache_config_dir_sync_max_write = (2 * 1024 * 1024);
int cache_config_dir_sync_parallel_tasks = 1;
int cache_config_permit_pinning = 0;
int cache_config_select_alternate = 1;
int cache_config_max_doc_size = 0;
int cache_config_min_average_object_size = ESTIMATED_OBJECT_SIZE;
int64_t cache_config_ram_cache_cutoff = AGG_SIZE;
int cache_config_max_disk_errors = 5;
int cache_config_hit_evacuate_percent = 10;
int cache_config_hit_evacuate_size_limit = 0;
int cache_config_force_sector_size = 0;
int cache_config_target_fragment_size = DEFAULT_TARGET_FRAGMENT_SIZE;
int cache_config_agg_write_backlog = AGG_SIZE * 2;
int cache_config_enable_checksum = 0;
int cache_config_alt_rewrite_max_size = 4096;
int cache_config_read_while_writer = 0;
int cache_config_mutex_retry_delay = 2;
int cache_read_while_writer_retry_delay = 50;
int cache_config_read_while_writer_max_retries = 10;
int cache_config_persist_bad_disks = false;

// Globals

Expand Down Expand Up @@ -865,6 +869,14 @@ ink_cache_init(ts::ModuleVersion v)
RecEstablishStaticConfigInt32(cache_config_ram_cache_compress_percent, "proxy.config.cache.ram_cache.compress_percent");
cache_config_ram_cache_use_seen_filter = RecGetRecordInt("proxy.config.cache.ram_cache.use_seen_filter").value_or(0);

RecEstablishStaticConfigInt32(cache_config_ram_cache_s3fifo_main_percent, "proxy.config.cache.ram_cache.s3fifo.main_percent");
RecEstablishStaticConfigInt32(cache_config_ram_cache_s3fifo_ghost_size_percent,
"proxy.config.cache.ram_cache.s3fifo.ghost_size_percent");
RecEstablishStaticConfigInt32(cache_config_ram_cache_s3fifo_ghost_mem_percent,
"proxy.config.cache.ram_cache.s3fifo.ghost_mem_percent");
RecEstablishStaticConfigInt32(cache_config_ram_cache_s3fifo_promote_threshold,
"proxy.config.cache.ram_cache.s3fifo.promote_threshold");

RecEstablishStaticConfigInt32(cache_config_http_max_alts, "proxy.config.cache.limits.http.max_alts");
Dbg(dbg_ctl_cache_init, "proxy.config.cache.limits.http.max_alts = %d", cache_config_http_max_alts);

Expand Down
7 changes: 7 additions & 0 deletions src/iocore/cache/CacheProcessor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1486,6 +1486,10 @@ CacheProcessor::cacheInitialized()

if (gnstripes) {
// new ram_caches, with algorithm from the config
static const char *const ram_cache_algorithm_name[] = {"CLFUS", "LRU", "S3-FIFO"};
int const ram_alg = cache_config_ram_cache_algorithm;
Dbg(dbg_ctl_cache_init, "ram_cache algorithm = %d (%s)", ram_alg,
(ram_alg >= 0 && ram_alg <= RAM_CACHE_ALGORITHM_S3FIFO) ? ram_cache_algorithm_name[ram_alg] : "unknown");
for (int i = 0; i < gnstripes; i++) {
switch (cache_config_ram_cache_algorithm) {
default:
Expand All @@ -1495,6 +1499,9 @@ CacheProcessor::cacheInitialized()
case RAM_CACHE_ALGORITHM_LRU:
gstripes[i]->ram_cache = new_RamCacheLRU();
break;
case RAM_CACHE_ALGORITHM_S3FIFO:
gstripes[i]->ram_cache = new_RamCacheS3FIFO();
break;
}
}

Expand Down
28 changes: 27 additions & 1 deletion src/iocore/cache/CacheTest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -679,8 +679,34 @@ REGRESSION_TEST(ram_cache)(RegressionTest *t, int level, int *pstatus)
for (int s = 20; s <= 24; s += 4) {
int64_t cache_size = 1LL << s;
*pstatus = REGRESSION_TEST_PASSED;
if (!test_RamCache(t, new_RamCacheLRU(), "LRU", cache_size) || !test_RamCache(t, new_RamCacheCLFUS(), "CLFUS", cache_size)) {
if (!test_RamCache(t, new_RamCacheLRU(), "LRU", cache_size) || !test_RamCache(t, new_RamCacheCLFUS(), "CLFUS", cache_size) ||
!test_RamCache(t, new_RamCacheS3FIFO(), "S3-FIFO", cache_size)) {
*pstatus = REGRESSION_TEST_FAILED;
}
}

// Exercise the S3-FIFO tunables with valid non-default values: the policy must still pass the
// hit-rate floor and the size invariant, proving the records -> init() config plumbing is wired
// and that a non-default queue split, ghost bound, and promotion threshold remain correct.
// (Out-of-range values are rejected by the records.yaml RECC_INT validation, not here.)
{
int const saved_main = cache_config_ram_cache_s3fifo_main_percent;
int const saved_gsize = cache_config_ram_cache_s3fifo_ghost_size_percent;
int const saved_gmem = cache_config_ram_cache_s3fifo_ghost_mem_percent;
int const saved_promote = cache_config_ram_cache_s3fifo_promote_threshold;
int64_t const cache_size = 1LL << 24;

cache_config_ram_cache_s3fifo_main_percent = 80; // valid, non-default
cache_config_ram_cache_s3fifo_ghost_size_percent = 50;
cache_config_ram_cache_s3fifo_ghost_mem_percent = 15;
cache_config_ram_cache_s3fifo_promote_threshold = 1;
if (!test_RamCache(t, new_RamCacheS3FIFO(), "S3-FIFO tuned", cache_size)) {
*pstatus = REGRESSION_TEST_FAILED;
}

cache_config_ram_cache_s3fifo_main_percent = saved_main;
cache_config_ram_cache_s3fifo_ghost_size_percent = saved_gsize;
cache_config_ram_cache_s3fifo_ghost_mem_percent = saved_gmem;
cache_config_ram_cache_s3fifo_promote_threshold = saved_promote;
}
}
4 changes: 4 additions & 0 deletions src/iocore/cache/P_CacheInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ extern int cache_config_agg_write_backlog;
extern int cache_config_ram_cache_compress;
extern int cache_config_ram_cache_compress_percent;
extern int cache_config_ram_cache_use_seen_filter;
extern int cache_config_ram_cache_s3fifo_main_percent;
extern int cache_config_ram_cache_s3fifo_ghost_size_percent;
extern int cache_config_ram_cache_s3fifo_ghost_mem_percent;
extern int cache_config_ram_cache_s3fifo_promote_threshold;
extern int cache_config_hit_evacuate_percent;
extern int cache_config_hit_evacuate_size_limit;
extern int cache_config_force_sector_size;
Expand Down
1 change: 1 addition & 0 deletions src/iocore/cache/P_RamCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ class RamCache

RamCache *new_RamCacheLRU();
RamCache *new_RamCacheCLFUS();
RamCache *new_RamCacheS3FIFO();
Loading