This is an automated email from the ASF dual-hosted git repository.
bryancall pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new d1d02c3f26 Fix CLFUS RAM cache value metric broken by integer division
(#13233)
d1d02c3f26 is described below
commit d1d02c3f2638554fff3eed95de01fa778aecf10b
Author: Phong Nguyen <[email protected]>
AuthorDate: Mon Jul 13 15:29:08 2026 -0700
Fix CLFUS RAM cache value metric broken by integer division (#13233)
PR #11733 rewrote the CACHE_VALUE_HITS_SIZE cast so static_cast<float>
wraps the whole quotient, making (hits + 1) / (size + overhead) integer
division. It truncates to 0 for normal object sizes, zeroing the value
metric and collapsing CLFUS to FIFO: no promote-on-hit, no clock second
chance, and no value-based ghost re-admission.
Bind the cast to the numerator to restore floating-point division, and
add the ram_cache_clfus_value regression test as a guard (it fails on
the pre-fix macro and passes after).
---
src/iocore/cache/RamCacheCLFUS.cc | 102 ++++++++++++++++++++++++++------------
1 file changed, 71 insertions(+), 31 deletions(-)
diff --git a/src/iocore/cache/RamCacheCLFUS.cc
b/src/iocore/cache/RamCacheCLFUS.cc
index 639d2faa33..feb09781eb 100644
--- a/src/iocore/cache/RamCacheCLFUS.cc
+++ b/src/iocore/cache/RamCacheCLFUS.cc
@@ -31,24 +31,25 @@
#include "iocore/eventsystem/Tasks.h"
#include "fastlz/fastlz.h"
#include "tscore/CryptoHash.h"
+#include "tscore/Regression.h"
#include <zlib.h>
#ifdef HAVE_LZMA_H
#include <lzma.h>
#endif
-#define REQUIRED_COMPRESSION 0.9 // must get to this size or declared
incompressible
-#define REQUIRED_SHRINK 0.8 // must get to this size or keep original
buffer (with padding)
-#define HISTORY_HYSTERIA 10 // extra temporary history
-#define ENTRY_OVERHEAD 256 // per-entry overhead to consider when
computing cache value/size
-#define LZMA_BASE_MEMLIMIT (64 * 1024 * 1024)
// #define CHECK_ACOUNTING 1 // very expensive double checking of all sizes
-#define REQUEUE_HITS(_h) ((_h) ? ((_h) - 1) : 0)
-#define CACHE_VALUE_HITS_SIZE(_h, _s) (static_cast<float>(((_h) + 1) / ((_s) +
ENTRY_OVERHEAD)))
-#define CACHE_VALUE(_x) CACHE_VALUE_HITS_SIZE((_x)->hits,
(_x)->size)
+constexpr double required_compression = 0.9;
+constexpr double required_shrink = 0.8;
+constexpr uint32_t history_hysteria = 10;
+constexpr uint32_t entry_overhead = 256; // per-entry overhead to
consider when computing cache value/size
-#define AVERAGE_VALUE_OVER 100
-#define REQUEUE_LIMIT 100
+#ifdef HAVE_LZMA_H
+constexpr uint32_t lzma_base_memlimit = 64 * 1024 * 1024;
+#endif
+
+constexpr uint32_t average_value_over = 100;
+constexpr uint32_t requeue_limit = 100;
#ifdef DEBUG
@@ -83,6 +84,24 @@ struct RamCacheCLFUSEntry {
Ptr<IOBufferData> data;
};
+constexpr uint64_t
+requeue_hits(const uint64_t hits)
+{
+ return hits ? (hits - 1) : 0;
+}
+
+constexpr double
+cache_value_hits_size(const uint64_t hits, const uint32_t size)
+{
+ return static_cast<double>(hits + 1) / (size + entry_overhead);
+}
+
+constexpr double
+cache_value(const RamCacheCLFUSEntry *const e)
+{
+ return cache_value_hits_size(e->hits, e->size);
+}
+
class RamCacheCLFUS : public RamCache
{
public:
@@ -230,7 +249,7 @@ check_accounting(RamCacheCLFUS *c)
RamCacheCLFUSEntry *y = c->lru[0].head;
while (y) {
x++;
- xsize += y->size + ENTRY_OVERHEAD;
+ xsize += y->size + entry_overhead;
y = y->lru_link.next;
}
y = c->lru[1].head;
@@ -259,7 +278,7 @@ RamCacheCLFUS::get(CryptoHash *key, Ptr<IOBufferData>
*ret_data, uint64_t auxkey
if (e->key == *key && e->auxkey == auxkey) {
this->_move_compressed(e);
if (!e->flag_bits.lru) { // in memory
- if (CACHE_VALUE(e) > this->_average_value) {
+ if (cache_value(e) > this->_average_value) {
this->_lru[e->flag_bits.lru].remove(e);
this->_lru[e->flag_bits.lru].enqueue(e);
}
@@ -290,7 +309,7 @@ RamCacheCLFUS::get(CryptoHash *key, Ptr<IOBufferData>
*ret_data, uint64_t auxkey
#ifdef HAVE_LZMA_H
case CACHE_COMPRESSION_LIBLZMA: {
size_t l = static_cast<size_t>(e->len), ipos = 0, opos = 0;
- uint64_t memlimit = e->len * 2 + LZMA_BASE_MEMLIMIT;
+ uint64_t memlimit = e->len * 2 + lzma_base_memlimit;
if (LZMA_OK != lzma_stream_buffer_decode(&memlimit, 0, nullptr,
reinterpret_cast<uint8_t *>(e->data->data()), &ipos,
e->compressed_len,
reinterpret_cast<uint8_t *>(b), &opos, l)) {
goto Lfailed;
@@ -357,12 +376,12 @@ RamCacheCLFUS::_tick()
}
e->hits >>= 1;
if (e->hits) {
- e->hits = REQUEUE_HITS(e->hits);
+ e->hits = requeue_hits(e->hits);
this->_lru[1].enqueue(e);
} else {
goto Lfree;
}
- if (this->_history <= this->_objects + HISTORY_HYSTERIA) {
+ if (this->_history <= this->_objects + history_hysteria) {
return;
}
e = this->_lru[1].dequeue();
@@ -410,7 +429,7 @@ RamCacheCLFUS::_destroy(RamCacheCLFUSEntry *e)
this->_lru[e->flag_bits.lru].remove(e);
if (!e->flag_bits.lru) {
this->_objects--;
- this->_bytes -= e->size + ENTRY_OVERHEAD;
+ this->_bytes -= e->size + entry_overhead;
ts::Metrics::Gauge::decrement(cache_rsb.ram_cache_bytes, e->size);
ts::Metrics::Gauge::decrement(stripe->cache_vol->vol_rsb.ram_cache_bytes,
e->size);
e->data = nullptr;
@@ -526,10 +545,10 @@ RamCacheCLFUS::compress_entries(EThread *thread, int
do_at_most)
goto Lcontinue;
}
}
- if (l > REQUIRED_COMPRESSION * e->len) {
+ if (l > required_compression * e->len) {
e->flag_bits.incompressible = true;
}
- if (l > REQUIRED_SHRINK * e->size) {
+ if (l > required_shrink * e->size) {
goto Lfailed;
}
if (l < e->len) {
@@ -581,10 +600,10 @@ RamCacheCLFUS::_requeue_victims(Que(RamCacheCLFUSEntry,
lru_link) & victims)
{
RamCacheCLFUSEntry *victim = nullptr;
while ((victim = victims.dequeue())) {
- this->_bytes += victim->size + ENTRY_OVERHEAD;
+ this->_bytes += victim->size + entry_overhead;
ts::Metrics::Gauge::increment(cache_rsb.ram_cache_bytes, victim->size);
ts::Metrics::Gauge::increment(stripe->cache_vol->vol_rsb.ram_cache_bytes,
victim->size);
- victim->hits = REQUEUE_HITS(victim->hits);
+ victim->hits = requeue_hits(victim->hits);
this->_lru[0].enqueue(victim);
}
}
@@ -637,7 +656,7 @@ RamCacheCLFUS::put(CryptoHash *key, IOBufferData *data,
uint32_t len, bool copy,
return 1;
} else {
this->_lru[1].remove(e);
- if (CACHE_VALUE(e) < this->_average_value) {
+ if (cache_value(e) < this->_average_value) {
this->_lru[1].enqueue(e);
return 0;
}
@@ -645,7 +664,7 @@ RamCacheCLFUS::put(CryptoHash *key, IOBufferData *data,
uint32_t len, bool copy,
}
Que(RamCacheCLFUSEntry, lru_link) victims;
RamCacheCLFUSEntry *victim = nullptr;
- int requeue_limit = REQUEUE_LIMIT;
+ int requeue_count = requeue_limit;
if (!this->_lru[1].head) { // initial fill
if (this->_bytes + size <= this->_max_bytes) {
goto Linsert;
@@ -674,12 +693,12 @@ RamCacheCLFUS::put(CryptoHash *key, IOBufferData *data,
uint32_t len, bool copy,
DDbg(dbg_ctl_ram_cache, "put %X %" PRId64 " NO VICTIM", key->slice32(3),
auxkey);
return 0;
}
- this->_average_value = (CACHE_VALUE(victim) + (this->_average_value *
(AVERAGE_VALUE_OVER - 1))) / AVERAGE_VALUE_OVER;
- if (CACHE_VALUE(victim) > this->_average_value && requeue_limit-- > 0) {
+ this->_average_value = (cache_value(victim) + (this->_average_value *
(average_value_over - 1))) / average_value_over;
+ if (cache_value(victim) > this->_average_value && requeue_count-- > 0) {
this->_lru[0].enqueue(victim);
continue;
}
- this->_bytes -= victim->size + ENTRY_OVERHEAD;
+ this->_bytes -= victim->size + entry_overhead;
ts::Metrics::Gauge::decrement(cache_rsb.ram_cache_bytes, victim->size);
ts::Metrics::Gauge::decrement(stripe->cache_vol->vol_rsb.ram_cache_bytes,
victim->size);
victims.enqueue(victim);
@@ -688,13 +707,13 @@ RamCacheCLFUS::put(CryptoHash *key, IOBufferData *data,
uint32_t len, bool copy,
} else {
this->_ncompressed--;
}
- victim_value += CACHE_VALUE(victim);
+ victim_value += cache_value(victim);
this->_tick();
if (!e) {
goto Lhistory;
} else { // e from history
- DDbg(dbg_ctl_ram_cache_compare, "put %f %f", victim_value,
CACHE_VALUE(e));
- if (this->_bytes + victim->size + size > this->_max_bytes &&
victim_value > CACHE_VALUE(e)) {
+ DDbg(dbg_ctl_ram_cache_compare, "put %f %f", victim_value,
cache_value(e));
+ if (this->_bytes + victim->size + size > this->_max_bytes &&
victim_value > cache_value(e)) {
this->_requeue_victims(victims);
this->_lru[1].enqueue(e);
DDbg(dbg_ctl_ram_cache, "put %X %" PRId64 " size %d INC %" PRId64 "
HISTORY", key->slice32(3), auxkey, e->size, e->hits);
@@ -708,10 +727,10 @@ RamCacheCLFUS::put(CryptoHash *key, IOBufferData *data,
uint32_t len, bool copy,
Linsert:
while ((victim = victims.dequeue())) {
if (this->_bytes + size + victim->size <= this->_max_bytes) {
- this->_bytes += victim->size + ENTRY_OVERHEAD;
+ this->_bytes += victim->size + entry_overhead;
ts::Metrics::Gauge::increment(cache_rsb.ram_cache_bytes, victim->size);
ts::Metrics::Gauge::increment(stripe->cache_vol->vol_rsb.ram_cache_bytes,
victim->size);
- victim->hits = REQUEUE_HITS(victim->hits);
+ victim->hits = requeue_hits(victim->hits);
this->_lru[0].enqueue(victim);
} else {
this->_victimize(victim);
@@ -741,7 +760,7 @@ Linsert:
e->data->_mem_type = DEFAULT_ALLOC;
}
e->flag_bits.copy = copy;
- this->_bytes += size + ENTRY_OVERHEAD;
+ this->_bytes += size + entry_overhead;
ts::Metrics::Gauge::increment(cache_rsb.ram_cache_bytes, size);
ts::Metrics::Gauge::increment(stripe->cache_vol->vol_rsb.ram_cache_bytes,
size);
e->size = size;
@@ -792,3 +811,24 @@ new_RamCacheCLFUS()
RamCacheCLFUS *r = new RamCacheCLFUS;
return r;
}
+
+// Guards against PR #11733-style regressions of the CLFUS value metric: the
value density
+// must be computed in floating point. Integer division truncates (hits + 1) /
(size + overhead)
+// to 0 for normal object sizes, zeroing the metric and silently collapsing
CLFUS to FIFO (no
+// promote-on-hit, no clock second chance, no value-based ghost re-admission).
+REGRESSION_TEST(ram_cache_clfus_value)([[maybe_unused]] RegressionTest *t,
[[maybe_unused]] int level, int *pstatus)
+{
+ *pstatus = REGRESSION_TEST_FAILED;
+
+ constexpr float v_one = cache_value_hits_size(1u, 16384u); // a typical
16 KiB object, seen once
+ constexpr float v_hot = cache_value_hits_size(100u, 16384u); // same size,
many more hits
+ constexpr float v_small = cache_value_hits_size(10u, 1024u); // smaller
object, equal hits
+ constexpr float v_large = cache_value_hits_size(10u, 16384u);
+
+ // A non-zero fraction: the integer-division regression makes this exactly
0.0f.
+ static_assert(v_one > 0.0f, "CLFUS value metric truncated to zero (integer
division)");
+ static_assert(v_hot > v_one, "CLFUS value metric does not increase with
hits");
+ static_assert(v_small > v_large, "CLFUS value metric does not decrease with
size");
+
+ *pstatus = REGRESSION_TEST_PASSED;
+}