Skip to content

Commit 6ae528e

Browse files
pbhandar2meta-codesync[bot]
authored andcommitted
Update access time map during DRAM evict of NVM clean item
Summary: Update the AccessTimeMap when we evict an nvm clean item which doesn't have to be rewritten to flash. This is done so that we preserve the last accessed timestamp of the item while not having to rewrite it to flash. Reviewed By: rlyerly Differential Revision: D96002667 fbshipit-source-id: 568b903c5c3839850de275bf9a2bf2f75ab5b05d
1 parent 432ffa7 commit 6ae528e

File tree

4 files changed

+156
-0
lines changed

4 files changed

+156
-0
lines changed

cachelib/allocator/CacheAllocator.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3951,6 +3951,14 @@ CacheAllocator<CacheTrait>::getNextCandidate(PoolId pid,
39513951
} else {
39523952
recordEvent(AllocatorApiEvent::DRAM_EVICT, candidate->getKey(),
39533953
AllocatorApiResult::EVICTED, candidate);
3954+
// When this item has an unmodified copy still present in BlockCache
3955+
// (large items only), record its latest DRAM access time in the Access
3956+
// Time Map as the value in the copy in BlockCache can be stale.
3957+
if (nvmCache_ && candidate->isNvmClean() && !candidate->isNvmEvicted() &&
3958+
candidate->isNvmLargeItem()) {
3959+
HashedKey hk{candidate->getKey()};
3960+
nvmCache_->updateAccessTime(hk, candidate->getLastAccessTime());
3961+
}
39543962
}
39553963
return {candidate, toRecycle};
39563964
}

cachelib/allocator/nvmcache/NvmCache.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,12 @@ class NvmCache {
304304
// and the item is present in NVM (NvmClean set and NvmEvicted flag unset).
305305
void markNvmItemRemovedLocked(HashedKey hk);
306306

307+
void updateAccessTime(HashedKey hk, uint32_t accessTimeSecs) {
308+
if (accessTimeMap_) {
309+
accessTimeMap_->set(hk.keyHash(), accessTimeSecs);
310+
}
311+
}
312+
307313
private:
308314
// Helper function to record event with NvmItem metadata
309315
// @param event The allocator API event type

cachelib/allocator/nvmcache/tests/NvmCacheTests.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3014,6 +3014,89 @@ TEST_F(NvmCacheTest, NvmLargeItemFlagOnPromotion) {
30143014
EXPECT_FALSE(testNvmLargeItemFlag("bh", 50, 'S'));
30153015
}
30163016

3017+
TEST_F(NvmCacheTest, AccessTimeMapPopulatedOnDramEviction) {
3018+
const int nKeys = 5;
3019+
const uint32_t allocSize = 15 * 1024;
3020+
3021+
insertPromoteAndEvictNvmCleanItems("atm_multi", allocSize, nKeys, allocSize);
3022+
3023+
// ATM should now have entries for the NvmClean items that were evicted
3024+
// from DRAM (BlockCache items only).
3025+
auto* atm = this->getAccessTimeMap();
3026+
auto now = util::getCurrentTimeSec();
3027+
int populated = 0;
3028+
for (int i = 0; i < nKeys; i++) {
3029+
auto key = folly::sformat("atm_multi_{}", i);
3030+
HashedKey hk{key};
3031+
auto ts = atm->get(hk.keyHash());
3032+
if (ts != std::nullopt) {
3033+
EXPECT_GT(*ts, 0);
3034+
EXPECT_LE(*ts, now);
3035+
++populated;
3036+
}
3037+
}
3038+
EXPECT_GT(populated, 0);
3039+
}
3040+
3041+
TEST_F(NvmCacheTest, AccessTimeMapNotUpdatedForBigHashItems) {
3042+
// Enable truncation so small items route to BigHash instead of BlockCache.
3043+
this->config_.setSimpleFile(cacheDir_ + "/navy", 200 * 1024ULL * 1024ULL,
3044+
true /* truncateFile */);
3045+
LruAllocator::NvmCacheConfig nvmConfig;
3046+
nvmConfig.navyConfig = this->config_;
3047+
nvmConfig.truncateItemToOriginalAllocSizeInNvm = true;
3048+
auto& config = this->getConfig();
3049+
config.enableNvmCache(nvmConfig);
3050+
this->poolAllocsizes_ = {20 * 1024};
3051+
this->makeCache();
3052+
3053+
const int nKeys = 5;
3054+
const uint32_t allocSize = 50;
3055+
const uint32_t fillerSize = 15 * 1024;
3056+
3057+
insertPromoteAndEvictNvmCleanItems("bh", allocSize, nKeys, fillerSize);
3058+
3059+
// ATM should NOT have entries for the BigHash items — the isNvmLargeItem()
3060+
// bit is not set for BigHash items, preventing updateAccessTime().
3061+
auto* atm = this->getAccessTimeMap();
3062+
for (int i = 0; i < nKeys; i++) {
3063+
auto key = folly::sformat("bh_{}", i);
3064+
HashedKey hk{key};
3065+
auto ts = atm->get(hk.keyHash());
3066+
EXPECT_EQ(std::nullopt, ts)
3067+
<< "BigHash item " << key << " should not be in ATM";
3068+
}
3069+
}
3070+
3071+
TEST_F(NvmCacheTest, AccessTimeMapNotUpdatedOnRegularEviction) {
3072+
auto& nvm = this->cache();
3073+
auto pid = this->poolId();
3074+
const uint32_t allocSize = 15 * 1024;
3075+
const uint32_t numKeysPerRegion =
3076+
config_.blockCache().getRegionSize() / allocSize;
3077+
3078+
auto* atm = this->getAccessTimeMap();
3079+
ASSERT_NE(nullptr, atm);
3080+
EXPECT_EQ(0, atm->size());
3081+
3082+
// Insert many fresh items (never been to NVM, so NOT NvmClean).
3083+
// Evictions of these items go through the NVM put path, not
3084+
// the updateAccessTime path.
3085+
for (int i = 0; i < 1024; i++) {
3086+
auto key = folly::sformat("regular_{}", i);
3087+
auto it = nvm.allocate(pid, key, allocSize);
3088+
ASSERT_NE(nullptr, it);
3089+
cache_->insertOrReplace(it);
3090+
if (i % numKeysPerRegion == 0) {
3091+
nvm.flushNvmCache();
3092+
}
3093+
}
3094+
nvm.flushNvmCache();
3095+
ASSERT_GT(this->evictionCount(), 0);
3096+
3097+
// Non-NvmClean evictions should NOT populate the AccessTimeMap.
3098+
EXPECT_EQ(0, atm->size());
3099+
}
30173100
} // namespace tests
30183101
} // namespace cachelib
30193102
} // namespace facebook

cachelib/allocator/nvmcache/tests/NvmTestBase.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <gtest/gtest.h>
2020

2121
#include "cachelib/allocator/CacheAllocator.h"
22+
#include "cachelib/allocator/nvmcache/AccessTimeMap.h"
2223
#include "cachelib/allocator/nvmcache/NavyConfig.h"
2324
#include "cachelib/common/Utils.h"
2425

@@ -132,6 +133,64 @@ class NvmCacheTest : public testing::Test {
132133
return cache_ ? cache_->nvmCache_.get() : nullptr;
133134
}
134135

136+
AccessTimeMap* getAccessTimeMap() {
137+
auto* nvm = getNvmCache();
138+
return nvm ? nvm->accessTimeMap_.get() : nullptr;
139+
}
140+
141+
// Helper: insert items, push to NVM, remove from RAM, promote (verify
142+
// NvmClean), then evict from DRAM by inserting fillers. Used by ATM tests
143+
// that share this common setup/eviction flow. Caller handles any non-default
144+
// cache setup beforehand and final ATM assertions afterward.
145+
void insertPromoteAndEvictNvmCleanItems(const std::string& keyPrefix,
146+
uint32_t itemAllocSize,
147+
int nKeys,
148+
uint32_t fillerAllocSize) {
149+
auto& nvm = cache();
150+
auto pid = poolId();
151+
152+
for (int i = 0; i < nKeys; i++) {
153+
auto key = folly::sformat("{}_{}", keyPrefix, i);
154+
auto it = nvm.allocate(pid, key, itemAllocSize);
155+
ASSERT_NE(nullptr, it);
156+
nvm.insertOrReplace(it);
157+
ASSERT_TRUE(pushToNvmCacheFromRamForTesting(key));
158+
}
159+
nvm.flushNvmCache();
160+
161+
for (int i = 0; i < nKeys; i++) {
162+
removeFromRamForTesting(folly::sformat("{}_{}", keyPrefix, i));
163+
}
164+
165+
auto* atm = getAccessTimeMap();
166+
ASSERT_NE(nullptr, atm);
167+
EXPECT_EQ(0, atm->size());
168+
169+
for (int i = 0; i < nKeys; i++) {
170+
auto key = folly::sformat("{}_{}", keyPrefix, i);
171+
auto hdl = fetch(key, false /* ramOnly */);
172+
ASSERT_NE(nullptr, hdl);
173+
ASSERT_TRUE(hdl->isNvmClean());
174+
}
175+
176+
EXPECT_EQ(0, atm->size());
177+
178+
const uint32_t numKeysPerRegion =
179+
config_.blockCache().getRegionSize() / fillerAllocSize;
180+
auto evictBefore = evictionCount();
181+
for (int i = 0; i < 1024; i++) {
182+
auto key = folly::sformat("filler_{}", i);
183+
auto it = nvm.allocate(pid, key, fillerAllocSize);
184+
ASSERT_NE(nullptr, it);
185+
cache_->insertOrReplace(it);
186+
if (i % numKeysPerRegion == 0) {
187+
nvm.flushNvmCache();
188+
}
189+
}
190+
nvm.flushNvmCache();
191+
ASSERT_GT(evictionCount(), evictBefore);
192+
}
193+
135194
std::unique_ptr<NvmItem> makeNvmItem(const Item& item) {
136195
return getNvmCache()->makeNvmItem(item);
137196
}

0 commit comments

Comments
 (0)