Skip to content

Commit 6aa4078

Browse files
committed
Expose 'eviction' and 'moving' states for an item
markForEviction is used only in findEviction and evictForSlabRelease but not for item movement. moveForSlabRelease relies on markMoving(). Only allow to mark item for eviction if ref count is 0. This ensures that after item is marked, eviction cannot fail. This makes it possible to return NULL handle immediately from find if item is marked for eviction. markMoving() does have those restrictions and still allows readers to obtain a handle to a moving item. Also, add option to use combined locking for MMContainer iteration. Pass item ref to NavyCache::put
1 parent 9fe02ed commit 6aa4078

File tree

14 files changed

+296
-589
lines changed

14 files changed

+296
-589
lines changed

cachelib/allocator/CacheAllocator-inl.h

Lines changed: 232 additions & 381 deletions
Large diffs are not rendered by default.

cachelib/allocator/CacheAllocator.h

Lines changed: 17 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,7 +1308,7 @@ class CacheAllocator : public CacheBase {
13081308

13091309
private:
13101310
// wrapper around Item's refcount and active handle tracking
1311-
FOLLY_ALWAYS_INLINE void incRef(Item& it);
1311+
FOLLY_ALWAYS_INLINE bool incRef(Item& it);
13121312
FOLLY_ALWAYS_INLINE RefcountWithFlags::Value decRef(Item& it);
13131313

13141314
// drops the refcount and if needed, frees the allocation back to the memory
@@ -1359,6 +1359,12 @@ class CacheAllocator : public CacheBase {
13591359
bool nascent = false,
13601360
const Item* toRecycle = nullptr);
13611361

1362+
// Must be called by the thread which called markForEviction and
1363+
// succeeded. After this call, the item is unlinked from Access and
1364+
// MM Containers. The item is no longer marked as exclusive and it's
1365+
// ref count is 0 - it's available for recycling.
1366+
void unlinkItemForEviction(Item& it);
1367+
13621368
// acquires an handle on the item. returns an empty handle if it is null.
13631369
// @param it pointer to an item
13641370
// @return WriteHandle return a handle to this item
@@ -1448,17 +1454,17 @@ class CacheAllocator : public CacheBase {
14481454
// @return handle to the parent item if the validations pass
14491455
// otherwise, an empty Handle is returned.
14501456
//
1451-
ReadHandle validateAndGetParentHandleForChainedMoveLocked(
1457+
WriteHandle validateAndGetParentHandleForChainedMoveLocked(
14521458
const ChainedItem& item, const Key& parentKey);
14531459

14541460
// Given an existing item, allocate a new one for the
14551461
// existing one to later be moved into.
14561462
//
1457-
// @param oldItem the item we want to allocate a new item for
1463+
// @param item reference to the item we want to allocate a new item for
14581464
//
14591465
// @return handle to the newly allocated item
14601466
//
1461-
WriteHandle allocateNewItemForOldItem(const Item& oldItem);
1467+
WriteHandle allocateNewItemForOldItem(const Item& item);
14621468

14631469
// internal helper that grabs a refcounted handle to the item. This does
14641470
// not record the access to reflect in the mmContainer.
@@ -1512,7 +1518,7 @@ class CacheAllocator : public CacheBase {
15121518
// callback is responsible for copying the contents and fixing the semantics
15131519
// of chained item.
15141520
//
1515-
// @param oldItem Reference to the item being moved
1521+
// @param oldItem item being moved
15161522
// @param newItemHdl Reference to the handle of the new item being moved into
15171523
//
15181524
// @return true If the move was completed, and the containers were updated
@@ -1662,25 +1668,6 @@ class CacheAllocator : public CacheBase {
16621668

16631669
using EvictionIterator = typename MMContainer::LockedIterator;
16641670

1665-
// Advance the current iterator and try to evict a regular item
1666-
//
1667-
// @param mmContainer the container to look for evictions.
1668-
// @param itr iterator holding the item
1669-
//
1670-
// @return valid handle to regular item on success. This will be the last
1671-
// handle to the item. On failure an empty handle.
1672-
WriteHandle advanceIteratorAndTryEvictRegularItem(MMContainer& mmContainer,
1673-
EvictionIterator& itr);
1674-
1675-
// Advance the current iterator and try to evict a chained item
1676-
// Iterator may also be reset during the course of this function
1677-
//
1678-
// @param itr iterator holding the item
1679-
//
1680-
// @return valid handle to the parent item on success. This will be the last
1681-
// handle to the item
1682-
WriteHandle advanceIteratorAndTryEvictChainedItem(EvictionIterator& itr);
1683-
16841671
// Deserializer CacheAllocatorMetadata and verify the version
16851672
//
16861673
// @param deserializer Deserializer object
@@ -1765,13 +1752,14 @@ class CacheAllocator : public CacheBase {
17651752
//
17661753
//
17671754
// @param ctx slab release context
1768-
// @param item old item to be moved elsewhere
1755+
// @param oldItem old item to be moved elsewhere
1756+
// @param handle handle to the item or to it's parent (if chained)
17691757
// @param throttler slow this function down as not to take too much cpu
17701758
//
17711759
// @return true if the item has been moved
17721760
// false if we have exhausted moving attempts
17731761
bool moveForSlabRelease(const SlabReleaseContext& ctx,
1774-
Item& item,
1762+
Item& oldItem,
17751763
util::Throttler& throttler);
17761764

17771765
// "Move" (by copying) the content in this item to another memory
@@ -1794,18 +1782,7 @@ class CacheAllocator : public CacheBase {
17941782
Item& item,
17951783
util::Throttler& throttler);
17961784

1797-
// Helper function to evict a normal item for slab release
1798-
//
1799-
// @return last handle for corresponding to item on success. empty handle on
1800-
// failure. caller can retry if needed.
1801-
WriteHandle evictNormalItemForSlabRelease(Item& item);
1802-
1803-
// Helper function to evict a child item for slab release
1804-
// As a side effect, the parent item is also evicted
1805-
//
1806-
// @return last handle to the parent item of the child on success. empty
1807-
// handle on failure. caller can retry.
1808-
WriteHandle evictChainedItemForSlabRelease(ChainedItem& item);
1785+
typename NvmCacheT::PutToken createPutToken(Item& item);
18091786

18101787
// Helper function to remove a item if expired.
18111788
//
@@ -1927,18 +1904,14 @@ class CacheAllocator : public CacheBase {
19271904
std::optional<bool> saveNvmCache();
19281905
void saveRamCache();
19291906

1930-
static bool itemExclusivePredicate(const Item& item) {
1931-
return item.getRefCount() == 0;
1907+
static bool itemSlabMovePredicate(const Item& item) {
1908+
return item.isMoving() && item.getRefCount() == 0;
19321909
}
19331910

19341911
static bool itemExpiryPredicate(const Item& item) {
19351912
return item.getRefCount() == 1 && item.isExpired();
19361913
}
19371914

1938-
static bool parentEvictForSlabReleasePredicate(const Item& item) {
1939-
return item.getRefCount() == 1 && !item.isMoving();
1940-
}
1941-
19421915
std::unique_ptr<Deserializer> createDeserializer();
19431916

19441917
// Execute func on each item. `func` can throw exception but must ensure

cachelib/allocator/MM2Q.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class MM2Q {
6868
enum LruType { Warm, WarmTail, Hot, Cold, ColdTail, NumTypes };
6969

7070
// Config class for MM2Q
71+
// TODO: implement support for useCombinedLockForIterators
7172
struct Config {
7273
// Create from serialized config
7374
explicit Config(SerializationConfigType configState)

cachelib/allocator/MMTinyLFU.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ class MMTinyLFU {
364364
Iterator(const Iterator&) = delete;
365365
Iterator& operator=(const Iterator&) = delete;
366366
Iterator(Iterator&&) noexcept = default;
367+
Iterator& operator=(Iterator&&) noexcept = default;
367368

368369
Iterator& operator++() noexcept {
369370
++getIter();
@@ -407,11 +408,7 @@ class MMTinyLFU {
407408
mIter_.resetToBegin();
408409
}
409410

410-
protected:
411-
// private because it's easy to misuse and cause deadlock for MMTinyLFU
412-
Iterator& operator=(Iterator&&) noexcept = default;
413-
414-
// create an lru iterator
411+
private:
415412
explicit Iterator(const Container<T, HookPtr>& c) noexcept;
416413

417414
const ListIterator& getIter() const noexcept {

cachelib/allocator/nvmcache/NvmCache-inl.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -460,19 +460,18 @@ uint32_t NvmCache<C>::getStorageSizeInNvm(const Item& it) {
460460
}
461461

462462
template <typename C>
463-
std::unique_ptr<NvmItem> NvmCache<C>::makeNvmItem(const WriteHandle& hdl) {
464-
const auto& item = *hdl;
463+
std::unique_ptr<NvmItem> NvmCache<C>::makeNvmItem(const Item& item) {
465464
auto poolId = cache_.getAllocInfo((void*)(&item)).poolId;
466465

467466
if (item.isChainedItem()) {
468467
throw std::invalid_argument(folly::sformat(
469-
"Chained item can not be flushed separately {}", hdl->toString()));
468+
"Chained item can not be flushed separately {}", item.toString()));
470469
}
471470

472471
auto chainedItemRange =
473-
CacheAPIWrapperForNvm<C>::viewAsChainedAllocsRange(cache_, *hdl);
472+
CacheAPIWrapperForNvm<C>::viewAsChainedAllocsRange(cache_, item);
474473
if (config_.encodeCb && !config_.encodeCb(EncodeDecodeContext{
475-
*(hdl.getInternal()), chainedItemRange})) {
474+
const_cast<Item&>(item), chainedItemRange})) {
476475
return nullptr;
477476
}
478477

@@ -496,12 +495,10 @@ std::unique_ptr<NvmItem> NvmCache<C>::makeNvmItem(const WriteHandle& hdl) {
496495
}
497496

498497
template <typename C>
499-
void NvmCache<C>::put(WriteHandle& hdl, PutToken token) {
498+
void NvmCache<C>::put(Item& item, PutToken token) {
500499
util::LatencyTracker tracker(stats().nvmInsertLatency_);
501-
HashedKey hk{hdl->getKey()};
500+
HashedKey hk{item.getKey()};
502501

503-
XDCHECK(hdl);
504-
auto& item = *hdl;
505502
// for regular items that can only write to nvmcache upon eviction, we
506503
// should not be recording a write for an nvmclean item unless it is marked
507504
// as evicted from nvmcache.
@@ -526,7 +523,7 @@ void NvmCache<C>::put(WriteHandle& hdl, PutToken token) {
526523
return;
527524
}
528525

529-
auto nvmItem = makeNvmItem(hdl);
526+
auto nvmItem = makeNvmItem(item);
530527
if (!nvmItem) {
531528
stats().numNvmPutEncodeFailure.inc();
532529
return;

cachelib/allocator/nvmcache/NvmCache.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,11 @@ class NvmCache {
158158
PutToken createPutToken(folly::StringPiece key);
159159

160160
// store the given item in navy
161-
// @param hdl handle to cache item. should not be null
161+
// @param item cache item
162162
// @param token the put token for the item. this must have been
163163
// obtained before enqueueing the put to maintain
164164
// consistency
165-
void put(WriteHandle& hdl, PutToken token);
165+
void put(Item& item, PutToken token);
166166

167167
// returns the current state of whether nvmcache is enabled or not. nvmcache
168168
// can be disabled if the backend implementation ends up in a corrupt state
@@ -286,7 +286,7 @@ class NvmCache {
286286
// returns true if there is tombstone entry for the key.
287287
bool hasTombStone(HashedKey hk);
288288

289-
std::unique_ptr<NvmItem> makeNvmItem(const WriteHandle& handle);
289+
std::unique_ptr<NvmItem> makeNvmItem(const Item& item);
290290

291291
// wrap an item into a blob for writing into navy.
292292
Blob makeBlob(const Item& it);

cachelib/allocator/nvmcache/tests/NvmTestBase.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class NvmCacheTest : public testing::Test {
108108
void pushToNvmCacheFromRamForTesting(WriteHandle& handle) {
109109
auto nvmCache = getNvmCache();
110110
if (nvmCache) {
111-
nvmCache->put(handle, nvmCache->createPutToken(handle->getKey()));
111+
nvmCache->put(*handle, nvmCache->createPutToken(handle->getKey()));
112112
}
113113
}
114114

@@ -127,7 +127,7 @@ class NvmCacheTest : public testing::Test {
127127
}
128128

129129
std::unique_ptr<NvmItem> makeNvmItem(const WriteHandle& handle) {
130-
return getNvmCache()->makeNvmItem(handle);
130+
return getNvmCache()->makeNvmItem(*handle);
131131
}
132132

133133
std::unique_ptr<folly::IOBuf> createItemAsIOBuf(folly::StringPiece key,

cachelib/allocator/tests/BaseAllocatorTest.h

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4080,15 +4080,16 @@ class BaseAllocatorTest : public AllocatorTest<AllocatorT> {
40804080
// Check that item is in the expected container.
40814081
bool findItem(AllocatorT& allocator, typename AllocatorT::Item* item) {
40824082
auto& container = allocator.getMMContainer(*item);
4083-
auto itr = container.getEvictionIterator();
40844083
bool found = false;
4085-
while (itr) {
4086-
if (itr.get() == item) {
4087-
found = true;
4088-
break;
4084+
container.withEvictionIterator([&found, &item](auto&& itr) {
4085+
while (itr) {
4086+
if (itr.get() == item) {
4087+
found = true;
4088+
break;
4089+
}
4090+
++itr;
40894091
}
4090-
++itr;
4091-
}
4092+
});
40924093
return found;
40934094
}
40944095

@@ -5476,8 +5477,12 @@ class BaseAllocatorTest : public AllocatorTest<AllocatorT> {
54765477
ASSERT_TRUE(big->isInMMContainer());
54775478

54785479
auto& mmContainer = alloc.getMMContainer(*big);
5479-
auto itr = mmContainer.getEvictionIterator();
5480-
ASSERT_EQ(big.get(), &(*itr));
5480+
5481+
typename AllocatorT::Item* evictionCandidate = nullptr;
5482+
mmContainer.withEvictionIterator(
5483+
[&evictionCandidate](auto&& itr) { evictionCandidate = itr.get(); });
5484+
5485+
ASSERT_EQ(big.get(), evictionCandidate);
54815486

54825487
alloc.remove("hello");
54835488
}
@@ -5491,8 +5496,11 @@ class BaseAllocatorTest : public AllocatorTest<AllocatorT> {
54915496
ASSERT_TRUE(small2->isInMMContainer());
54925497

54935498
auto& mmContainer = alloc.getMMContainer(*small2);
5494-
auto itr = mmContainer.getEvictionIterator();
5495-
ASSERT_EQ(small2.get(), &(*itr));
5499+
5500+
typename AllocatorT::Item* evictionCandidate = nullptr;
5501+
mmContainer.withEvictionIterator(
5502+
[&evictionCandidate](auto&& itr) { evictionCandidate = itr.get(); });
5503+
ASSERT_EQ(small2.get(), evictionCandidate);
54965504

54975505
alloc.remove("hello");
54985506
}

0 commit comments

Comments
 (0)