Skip to content

Commit fe822e2

Browse files
pbhandar2meta-codesync[bot]
authored andcommitted
Track TTA of items on NVM hit as ODS stat
Summary: Add a percentile stat tracking Time-To-Access (TTA) — how long ago items were last accessed before being looked up from NVM. This helps understand working set recency: for each NVM cache hit, how stale the item was when it was re-accessed. The `lastAccessTimeSecs` is already stored in Navy's `BlockCache::EntryDesc` on disk (set during DRAM→NVM eviction) and plumbed through the Navy lookup path all the way to `NvmCache::onGetComplete()`. No new plumbing is needed. Changes: - Declare `nvmHitTTASecs_` PercentileStats field in `detail::Stats` (CacheStatsInternal.h). - Declare `nvmHitTTASecs` Estimates field in `GlobalCacheStats` (CacheStats.h). - Populate the estimate in `Stats::populateGlobalCacheStats()` and update the `EXPECTED_SIZE` compile-time check (CacheStats.cpp). - Track TTA in `NvmCache::onGetComplete()` after all early-return checks confirm a genuine NVM hit. Guarded by `lastAccessTimeSecs > 0` since BigHash items don't store access time (NvmCache.h). - Export via `visitEstimates()` as `nvm.hit_tta_secs` with standard percentile suffixes (_p50, _p99, _avg, etc.) (Cache.cpp). Reviewed By: AlnisM Differential Revision: D94783700 fbshipit-source-id: 4000027e7c2bd2bfd9eeb448901ddce94a2ffef7
1 parent 3db8471 commit fe822e2

6 files changed

Lines changed: 60 additions & 5 deletions

File tree

cachelib/allocator/Cache.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,8 @@ void CacheBase::updateGlobalCacheStats(const std::string& statPrefix) const {
481481
statPrefix + "nvm.make_obj_cb.latency_us");
482482
visitEstimates(uploadStats, stats.nvmPutSize,
483483
statPrefix + "nvm.incoming_item_size_bytes");
484+
visitEstimates(uploadStats, stats.nvmHitTTASecs,
485+
statPrefix + "nvm.hit_tta_secs");
484486

485487
if (stats.numNvmDestructorRefcountOverflow > 0) {
486488
counters_.updateCount(statPrefix + "nvm.destructors.refcount_overflow",

cachelib/allocator/CacheStats.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ struct SizeVerify {};
5454
void Stats::populateGlobalCacheStats(GlobalCacheStats& ret) const {
5555
#ifndef SKIP_SIZE_VERIFY
5656
#ifdef __GLIBCXX__
57-
#define EXPECTED_SIZE 16736
57+
#define EXPECTED_SIZE 16912
5858
#endif
5959
#ifdef _LIBCPP_VERSION
60-
#define EXPECTED_SIZE 16736
60+
#define EXPECTED_SIZE 16912
6161
#endif
6262
SizeVerify<sizeof(Stats)> a = SizeVerify<EXPECTED_SIZE>{};
6363
std::ignore = a;
@@ -125,6 +125,7 @@ void Stats::populateGlobalCacheStats(GlobalCacheStats& ret) const {
125125
this->nvmEvictionSecondsPastExpiry_.estimate();
126126
ret.nvmEvictionSecondsToExpiry = this->nvmEvictionSecondsToExpiry_.estimate();
127127
ret.nvmPutSize = this->nvmPutSize_.estimate();
128+
ret.nvmHitTTASecs = this->nvmHitTTASecs_.estimate();
128129

129130
auto accum = [](const PerPoolClassAtomicCounters& c) {
130131
uint64_t sum = 0;

cachelib/allocator/CacheStats.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ struct GlobalCacheStats {
527527
util::PercentileStats::Estimates nvmEvictionSecondsPastExpiry{};
528528
util::PercentileStats::Estimates nvmEvictionSecondsToExpiry{};
529529
util::PercentileStats::Estimates nvmPutSize{};
530+
util::PercentileStats::Estimates nvmHitTTASecs{};
530531

531532
// time when CacheAllocator structure is created. Whenever a process restarts
532533
// and even if cache content is persisted, this will be reset. It's similar

cachelib/allocator/CacheStatsInternal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ struct Stats {
209209
mutable util::PercentileStats nvmLargeLifetimeSecs_;
210210
mutable util::PercentileStats nvmEvictionSecondsPastExpiry_;
211211
mutable util::PercentileStats nvmEvictionSecondsToExpiry_;
212+
mutable util::PercentileStats nvmHitTTASecs_;
212213

213214
// per-pool percentile stats for eviction age
214215
std::array<util::PercentileStats, MemoryPoolManager::kMaxPools>

cachelib/allocator/nvmcache/NvmCache.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,9 +1271,6 @@ void NvmCache<C>::onGetComplete(GetCtx& ctx,
12711271
HashedKey hk,
12721272
navy::BufferView val,
12731273
uint32_t lastAccessTimeSecs) {
1274-
// lastAccessTimeSecs will be used by retention threshold enforcement
1275-
// in a follow-up diff.
1276-
(void)lastAccessTimeSecs;
12771274
auto guard =
12781275
folly::makeGuard([&ctx, hk]() { ctx.cache.removeFromFillMap(hk); });
12791276
// navy got disabled while we were fetching. If so, safely return a miss.
@@ -1338,6 +1335,16 @@ void NvmCache<C>::onGetComplete(GetCtx& ctx,
13381335

13391336
recordEvent(AllocatorApiEvent::NVM_FIND, hk.key(), AllocatorApiResult::FOUND,
13401337
nvmItem);
1338+
1339+
// Track NVM hit time-to-access for every NVM hit, regardless of whether
1340+
// the DRAM promotion succeeds (another thread may have already promoted).
1341+
// TTA = currentTime - lastAccessTimeSecs (how long ago item was last
1342+
// accessed). Guard > 0 because BigHash doesn't store access time.
1343+
if (lastAccessTimeSecs > 0) {
1344+
auto ttaSecs = util::getCurrentTimeSec() - lastAccessTimeSecs;
1345+
stats().nvmHitTTASecs_.trackValue(ttaSecs);
1346+
}
1347+
13411348
// by the time we filled from navy, another thread inserted in RAM. We
13421349
// disregard.
13431350
if (CacheAPIWrapperForNvm<C>::insertFromNvm(cache_, it)) {

cachelib/allocator/nvmcache/tests/NvmCacheTests.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2925,6 +2925,49 @@ TEST_F(NvmCacheTest, DataCorruption) {
29252925
EXPECT_EQ(nullptr, it);
29262926
}
29272927
}
2928+
2929+
TEST_F(NvmCacheTest, NvmHitTTATracking) {
2930+
// Disable BigHash — it doesn't store lastAccessTimeSecs (returns 0).
2931+
// BlockCache tracks it, so TTA can be computed.
2932+
this->config_.bigHash().setSizePctAndMaxItemSize(0, 100);
2933+
LruAllocator::NvmCacheConfig nvmConfig;
2934+
nvmConfig.navyConfig = config_;
2935+
this->allocConfig_.enableNvmCache(nvmConfig);
2936+
this->makeCache();
2937+
2938+
auto& cache = this->cache();
2939+
auto pid = this->poolId();
2940+
2941+
std::string key = "tta_test_key";
2942+
std::string val = "tta_test_value";
2943+
2944+
// 1. Insert item into DRAM
2945+
{
2946+
auto it = cache.allocate(pid, key, val.length());
2947+
ASSERT_NE(nullptr, it);
2948+
::memcpy(it->getMemory(), val.data(), val.length());
2949+
cache.insertOrReplace(it);
2950+
}
2951+
2952+
// 2. Push to NVM and remove from RAM
2953+
this->pushToNvmCacheFromRamForTesting(key);
2954+
this->removeFromRamForTesting(key);
2955+
2956+
// 3. Wait so TTA (currentTime - lastAccessTimeSecs) > 0
2957+
/* sleep override */ std::this_thread::sleep_for(std::chrono::seconds(1));
2958+
2959+
// 4. Fetch from NVM (triggers onGetComplete -> TTA tracking)
2960+
auto it = this->fetch(key, false /* ramOnly */);
2961+
ASSERT_NE(nullptr, it);
2962+
2963+
// 5. Verify NVM hit occurred
2964+
auto stats = this->getStats();
2965+
EXPECT_GT(stats.numNvmGets, 0);
2966+
EXPECT_GT(stats.numNvmGets - stats.numNvmGetMiss, 0);
2967+
2968+
// 6. Verify TTA was tracked (>= 1 second)
2969+
EXPECT_GE(stats.nvmHitTTASecs.p50, 1);
2970+
}
29282971
} // namespace tests
29292972
} // namespace cachelib
29302973
} // namespace facebook

0 commit comments

Comments
 (0)