Skip to content
Merged
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
4 changes: 2 additions & 2 deletions cachelib/allocator/CacheAllocator-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1308,15 +1308,15 @@ CacheAllocator<CacheTrait>::moveRegularItemWithSync(
// make sure that no other thread removed it, and only then replaces it.
if (!replaceInMMContainer(oldItem, *newItemHdl)) {
accessContainer_->remove(*newItemHdl);
return {};
return acquire(&oldItem);
}

// Replacing into the MM container was successful, but someone could have
// called insertOrReplace() or remove() before or after the
// replaceInMMContainer() operation, which would invalidate newItemHdl.
if (!newItemHdl->isAccessible()) {
removeFromMMContainer(*newItemHdl);
return {};
return acquire(&oldItem);
}

// no one can add or remove chained items at this point
Expand Down
5 changes: 3 additions & 2 deletions cachelib/allocator/CacheAllocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -1496,8 +1496,9 @@ class CacheAllocator : public CacheBase {
// @param oldItem Reference to the item being moved
// @param newItemHdl Reference to the handle of the new item being moved into
//
// @return true If the move was completed, and the containers were updated
// successfully.
// @return the handle to the oldItem if the move was completed
// and the oldItem can be recycled.
// Otherwise an empty handle is returned.
template <typename P>
WriteHandle moveRegularItemWithSync(Item& oldItem, WriteHandle& newItemHdl, P&& predicate);

Expand Down
2 changes: 2 additions & 0 deletions cachelib/allocator/tests/AllocatorMemoryTiersTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ TEST_F(LruAllocatorMemoryTiersTest, MultiTiersFromFileValid) { this->testMultiTi
TEST_F(LruAllocatorMemoryTiersTest, MultiTiersValidMixed) { this->testMultiTiersValidMixed(); }
TEST_F(LruAllocatorMemoryTiersTest, MultiTiersNumaBindingsSysVValid) { this->testMultiTiersNumaBindingsSysVValid(); }
TEST_F(LruAllocatorMemoryTiersTest, MultiTiersNumaBindingsPosixValid) { this->testMultiTiersNumaBindingsPosixValid(); }
TEST_F(LruAllocatorMemoryTiersTest, MultiTiersRemoveDuringEviction) { this->testMultiTiersRemoveDuringEviction(); }
TEST_F(LruAllocatorMemoryTiersTest, MultiTiersReplaceDuringEviction) { this->testMultiTiersReplaceDuringEviction(); }

} // end of namespace tests
} // end of namespace cachelib
Expand Down
94 changes: 94 additions & 0 deletions cachelib/allocator/tests/AllocatorMemoryTiersTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,42 @@
#include "cachelib/allocator/MemoryTierCacheConfig.h"
#include "cachelib/allocator/tests/TestBase.h"

#include <folly/synchronization/Latch.h>

namespace facebook {
namespace cachelib {
namespace tests {

template <typename AllocatorT>
class AllocatorMemoryTiersTest : public AllocatorTest<AllocatorT> {
private:
template<typename MvCallback>
void testMultiTiersAsyncOpDuringMove(std::unique_ptr<AllocatorT>& alloc,
PoolId& pool, bool& quit, MvCallback&& moveCb) {
typename AllocatorT::Config config;
config.setCacheSize(4 * Slab::kSize);
config.enableCachePersistence("/tmp");
config.configureMemoryTiers({
MemoryTierCacheConfig::fromShm()
.setRatio(1).setMemBind({0}),
MemoryTierCacheConfig::fromShm()
.setRatio(1).setMemBind({0})
});

config.enableMovingOnSlabRelease(moveCb, {} /* ChainedItemsMoveSync */,
-1 /* movingAttemptsLimit */);

alloc = std::make_unique<AllocatorT>(AllocatorT::SharedMemNew, config);
ASSERT(alloc != nullptr);
pool = alloc->addPool("default", alloc->getCacheMemoryStats().cacheSize);

int i = 0;
while(!quit) {
auto handle = alloc->allocate(pool, std::to_string(++i), std::string("value").size());
ASSERT(handle != nullptr);
ASSERT_NO_THROW(alloc->insertOrReplace(handle));
}
}
public:
void testMultiTiersFormFileInvalid() {
typename AllocatorT::Config config;
Expand Down Expand Up @@ -124,6 +154,70 @@ class AllocatorMemoryTiersTest : public AllocatorTest<AllocatorT> {
ASSERT(handle != nullptr);
ASSERT_NO_THROW(alloc->insertOrReplace(handle));
}

void testMultiTiersRemoveDuringEviction() {
std::unique_ptr<AllocatorT> alloc;
PoolId pool;
std::unique_ptr<std::thread> t;
folly::Latch latch(1);
bool quit = false;

auto moveCb = [&] (typename AllocatorT::Item& oldItem,
typename AllocatorT::Item& newItem,
typename AllocatorT::Item* /* parentPtr */) {

auto key = oldItem.getKey();
t = std::make_unique<std::thread>([&](){
// remove() function is blocked by wait context
// till item is moved to next tier. So that, we should
// notify latch before calling remove()
latch.count_down();
alloc->remove(key);
});
// wait till async thread is running
latch.wait();
memcpy(newItem.getMemory(), oldItem.getMemory(), oldItem.getSize());
quit = true;
};

testMultiTiersAsyncOpDuringMove(alloc, pool, quit, moveCb);

t->join();
}

void testMultiTiersReplaceDuringEviction() {
std::unique_ptr<AllocatorT> alloc;
PoolId pool;
std::unique_ptr<std::thread> t;
folly::Latch latch(1);
bool quit = false;

auto moveCb = [&] (typename AllocatorT::Item& oldItem,
typename AllocatorT::Item& newItem,
typename AllocatorT::Item* /* parentPtr */) {
auto key = oldItem.getKey();
if(!quit) {
// we need to replace only once because subsequent allocate calls
// will cause evictions recursevly
quit = true;
t = std::make_unique<std::thread>([&](){
auto handle = alloc->allocate(pool, key, std::string("new value").size());
// insertOrReplace() function is blocked by wait context
// till item is moved to next tier. So that, we should
// notify latch before calling insertOrReplace()
latch.count_down();
ASSERT_NO_THROW(alloc->insertOrReplace(handle));
});
// wait till async thread is running
latch.wait();
}
memcpy(newItem.getMemory(), oldItem.getMemory(), oldItem.getSize());
};

testMultiTiersAsyncOpDuringMove(alloc, pool, quit, moveCb);

t->join();
}
};
} // namespace tests
} // namespace cachelib
Expand Down