Skip to content

Commit d085d86

Browse files
authored
feat(store): Add LRU in MasterService, complexity O(1) (#287)
* Fix nvmeof build issue * Add LRU in Master Service, complexity O(1) * Move Macros to CMake Options * fix lru build options * Now we can setup LRU_MAX_CAPACITY with cmake commands * Refactoring LRU to EvictionStrategy Class * Add tests of eviction strategy * fix build issues * Fix eviction strategy test issue * Resolve conflicts with master * resolve conflicts, second part * Change MasterMetrics to get ratio of used storage and total capacity, if more than 80% used, trigger evict
1 parent a700e9d commit d085d86

File tree

10 files changed

+280
-3
lines changed

10 files changed

+280
-3
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ project(mooncake CXX C)
55
set(GLOBAL_CONFIG "true")
66

77
include(mooncake-common/common.cmake)
8-
98
# unit test
109
if (BUILD_UNIT_TESTS)
1110
enable_testing()
@@ -23,6 +22,7 @@ execute_process(
2322
)
2423
string(STRIP ${PYTHON_SYS_PATH} PYTHON_SYS_PATH)
2524

25+
2626
if (USE_ETCD)
2727
add_compile_definitions(USE_ETCD)
2828
if (USE_ETCD_LEGACY)

mooncake-common/common.cmake

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ option(USE_HTTP "option for enable http as metadata server" ON)
4646
option(WITH_RUST_EXAMPLE "build the Rust interface and sample code for the transfer engine" OFF)
4747
option(WITH_METRICS "enable metrics and metrics reporting thread" ON)
4848

49+
50+
option(USE_LRU_MASTER "option for using LRU in master service" OFF)
51+
set(LRU_MAX_CAPACITY 1000)
52+
53+
if (USE_LRU_MASTER)
54+
add_compile_definitions(USE_LRU_MASTER)
55+
add_compile_definitions(LRU_MAX_CAPACITY)
56+
endif()
57+
58+
4959
if (USE_CUDA)
5060
add_compile_definitions(USE_CUDA)
5161
message(STATUS "CUDA support is enabled")
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#pragma once
2+
3+
#include <memory>
4+
#include <random>
5+
#include <string>
6+
#include <unordered_map>
7+
8+
#include "types.h"
9+
10+
namespace mooncake {
11+
12+
/**
13+
* @brief Abstract interface for eviction strategy, responsible for choosing
14+
* which kvcache object to be evicted before pool overflow.
15+
*/
16+
class EvictionStrategy : public std::enable_shared_from_this<EvictionStrategy>{
17+
public:
18+
virtual ~EvictionStrategy() = default;
19+
virtual ErrorCode AddKey(const std::string& key) = 0;
20+
virtual ErrorCode UpdateKey(const std::string& key) = 0;
21+
virtual ErrorCode RemoveKey(const std::string& key) {
22+
// Remove key from the list and map
23+
auto it = all_key_idx_map_.find(key);
24+
if (it != all_key_idx_map_.end()) {
25+
all_key_list_.erase(it->second);
26+
all_key_idx_map_.erase(it);
27+
}
28+
return ErrorCode::OK;
29+
}
30+
virtual std::string EvictKey(void) = 0;
31+
virtual size_t GetSize(void) {
32+
return all_key_list_.size();
33+
}
34+
void CleanUp(void) {
35+
all_key_list_.clear();
36+
all_key_idx_map_.clear();
37+
}
38+
protected:
39+
std::list<std::string> all_key_list_;
40+
std::unordered_map<std::string, std::list<std::string>::iterator> all_key_idx_map_;
41+
};
42+
43+
class LRUEvictionStrategy : public EvictionStrategy {
44+
public:
45+
virtual ErrorCode AddKey(const std::string& key) override {
46+
// Add key to the front of the list
47+
if(all_key_idx_map_.find(key) != all_key_idx_map_.end()) {
48+
all_key_list_.erase(all_key_idx_map_[key]);
49+
all_key_idx_map_.erase(key);
50+
}
51+
all_key_list_.push_front(key);
52+
all_key_idx_map_[key] = all_key_list_.begin();
53+
return ErrorCode::OK;
54+
}
55+
56+
virtual ErrorCode UpdateKey(const std::string& key) override {
57+
// Move the key to the front of the list
58+
auto it = all_key_idx_map_.find(key);
59+
if (it != all_key_idx_map_.end()) {
60+
all_key_list_.erase(it->second);
61+
all_key_list_.push_front(key);
62+
all_key_idx_map_[key] = all_key_list_.begin();
63+
}
64+
return ErrorCode::OK;
65+
}
66+
67+
virtual std::string EvictKey(void) override {
68+
// Evict the last key in the list
69+
if (all_key_list_.empty()) {
70+
return "";
71+
}
72+
std::string evicted_key = all_key_list_.back();
73+
all_key_list_.pop_back();
74+
all_key_idx_map_.erase(evicted_key);
75+
return evicted_key;
76+
}
77+
};
78+
79+
class FIFOEvictionStrategy : public EvictionStrategy {
80+
public:
81+
virtual ErrorCode AddKey(const std::string& key) override {
82+
// Add key to the front of the list
83+
all_key_list_.push_front(key);
84+
return ErrorCode::OK;
85+
}
86+
virtual ErrorCode UpdateKey(const std::string& key) override {
87+
return ErrorCode::OK;
88+
}
89+
virtual std::string EvictKey(void) {
90+
if (all_key_list_.empty()) {
91+
return "";
92+
}
93+
std::string evicted_key = all_key_list_.back();
94+
all_key_list_.pop_back();
95+
return evicted_key;
96+
}
97+
};
98+
99+
}

mooncake-store/include/master_metric_manager.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class MasterMetricManager {
2424
void dec_allocated_size(int64_t val = 1.0);
2525
void inc_total_capacity(int64_t val = 1.0);
2626
void dec_total_capacity(int64_t val = 1.0);
27+
double get_global_used_ratio(void);
2728

2829
// Key/Value Metrics
2930
void inc_key_count(int64_t val = 1.0);

mooncake-store/include/master_service.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@
1212
#include <vector>
1313

1414
#include "allocation_strategy.h"
15+
#include "eviction_strategy.h"
1516
#include "allocator.h"
1617
#include "types.h"
1718

19+
1820
namespace mooncake {
1921
// Forward declarations
2022
class AllocationStrategy;
23+
class EvictionStrategy;
2124

2225
// Structure to store garbage collection tasks
2326
struct GCTask {
@@ -185,6 +188,8 @@ class MasterService {
185188
// Buffer allocator management
186189
std::shared_ptr<BufferAllocatorManager> buffer_allocator_manager_;
187190
std::shared_ptr<AllocationStrategy> allocation_strategy_;
191+
std::shared_ptr<EvictionStrategy> eviction_strategy_;
192+
188193

189194
static constexpr size_t kNumShards = 1024; // Number of metadata shards
190195

mooncake-store/src/master_metric_manager.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ void MasterMetricManager::dec_total_capacity(int64_t val) {
8989
total_capacity_.dec(val);
9090
}
9191

92+
double MasterMetricManager::get_global_used_ratio(void) {
93+
double allocated = allocated_size_.value();
94+
double capacity = total_capacity_.value();
95+
return allocated / capacity;
96+
}
97+
9298
// Key/Value Metrics
9399
void MasterMetricManager::inc_key_count(int64_t val) { key_count_.inc(val); }
94100
void MasterMetricManager::dec_key_count(int64_t val) { key_count_.dec(val); }

mooncake-store/src/master_service.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ ErrorCode BufferAllocatorManager::RemoveSegment(
6969
MasterService::MasterService(bool enable_gc)
7070
: buffer_allocator_manager_(std::make_shared<BufferAllocatorManager>()),
7171
allocation_strategy_(std::make_shared<RandomAllocationStrategy>()),
72+
eviction_strategy_(std::make_shared<LRUEvictionStrategy>()),
7273
enable_gc_(enable_gc) {
7374
// Start the GC thread if enabled
7475
if (enable_gc_) {
@@ -78,6 +79,7 @@ MasterService::MasterService(bool enable_gc)
7879
} else {
7980
VLOG(1) << "action=gc_disabled";
8081
}
82+
eviction_strategy_ -> CleanUp();
8183
}
8284

8385
MasterService::~MasterService() {
@@ -94,6 +96,10 @@ MasterService::~MasterService() {
9496
delete task;
9597
}
9698
}
99+
100+
LOG(INFO) << "### LRU cleared ###";
101+
eviction_strategy_ -> CleanUp();
102+
97103
}
98104

99105
ErrorCode MasterService::MountSegment(uint64_t buffer, uint64_t size,
@@ -163,9 +169,11 @@ ErrorCode MasterService::GetReplicaList(
163169
MetadataAccessor accessor(this, key);
164170
if (!accessor.Exists()) {
165171
VLOG(1) << "key=" << key << ", info=object_not_found";
172+
// If the object is not found, we should synchronize the EvictionStrategy
173+
eviction_strategy_->RemoveKey(key);
166174
return ErrorCode::OBJECT_NOT_FOUND;
167175
}
168-
176+
eviction_strategy_->UpdateKey(key);
169177
auto& metadata = accessor.Get();
170178
for (const auto& replica : metadata.replicas) {
171179
auto status = replica.status();
@@ -201,6 +209,9 @@ ErrorCode MasterService::PutStart(
201209
return ErrorCode::INVALID_PARAMS;
202210
}
203211

212+
LOG(INFO) << "### LRU Update in Put() ###";
213+
eviction_strategy_->AddKey(key);
214+
204215
// Validate slice lengths
205216
uint64_t total_length = 0;
206217
for (size_t i = 0; i < slice_lengths.size(); ++i) {
@@ -299,6 +310,14 @@ ErrorCode MasterService::PutEnd(const std::string& key) {
299310
for (auto& replica : metadata.replicas) {
300311
replica.mark_complete();
301312
}
313+
314+
// if globally used storage ratio >= 80%, evict some of them
315+
if(MasterMetricManager::instance().get_global_used_ratio() >= 0.8) {
316+
std::string evicted_key = eviction_strategy_->EvictKey();
317+
LOG(INFO) << "### LRU action! Evicted key = " << evicted_key << " ###";
318+
Remove(evicted_key);
319+
}
320+
302321
return ErrorCode::OK;
303322
}
304323

mooncake-store/tests/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ add_executable(buffer_allocator_test buffer_allocator_test.cpp)
22
target_link_libraries(buffer_allocator_test PUBLIC mooncake_store cachelib_memory_allocator gtest gtest_main pthread)
33
add_test(NAME buffer_allocator_test COMMAND buffer_allocator_test)
44

5+
add_executable(eviction_strategy_test eviction_strategy_test.cpp)
6+
target_link_libraries(eviction_strategy_test PUBLIC mooncake_store cachelib_memory_allocator glog gtest gtest_main pthread)
7+
add_test(NAME eviction_strategy_test COMMAND eviction_strategy_test)
8+
59
add_executable(master_service_test master_service_test.cpp)
610
target_link_libraries(master_service_test PUBLIC mooncake_store cachelib_memory_allocator glog gtest gtest_main pthread)
711
add_test(NAME master_service_test COMMAND master_service_test)
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// eviction_strategy_test.cpp
2+
#include <glog/logging.h>
3+
#include <gtest/gtest.h>
4+
5+
#include <memory>
6+
7+
#include "eviction_strategy.h"
8+
9+
namespace mooncake {
10+
11+
// Test fixture for EvictionStrategy tests
12+
class EvictionStrategyTest : public ::testing::Test {
13+
protected:
14+
void SetUp() override {
15+
// Initialize glog for logging
16+
google::InitGoogleLogging("EvictionStrategyTest");
17+
FLAGS_logtostderr = 1; // Output logs to stderr
18+
}
19+
20+
void TearDown() override {
21+
// Cleanup glog
22+
google::ShutdownGoogleLogging();
23+
}
24+
};
25+
26+
27+
// Test LRUEvictionStrategy AddKey and RemoveKey functionality
28+
TEST_F(EvictionStrategyTest, AddAndRemoveKey) {
29+
LRUEvictionStrategy eviction_strategy;
30+
31+
// Add keys
32+
EXPECT_EQ(eviction_strategy.AddKey("key1"), ErrorCode::OK);
33+
EXPECT_EQ(eviction_strategy.AddKey("key2"), ErrorCode::OK);
34+
35+
// Check size
36+
EXPECT_EQ(eviction_strategy.GetSize(), 2);
37+
38+
// Remove a key
39+
EXPECT_EQ(eviction_strategy.RemoveKey("key1"), ErrorCode::OK);
40+
EXPECT_EQ(eviction_strategy.GetSize(), 1);
41+
42+
// Clean up
43+
eviction_strategy.CleanUp();
44+
}
45+
46+
// Test LRUEvictionStrategy EvictKey functionality
47+
TEST_F(EvictionStrategyTest, EvictKey) {
48+
LRUEvictionStrategy eviction_strategy;
49+
50+
// Add keys
51+
EXPECT_EQ(eviction_strategy.AddKey("key1"), ErrorCode::OK);
52+
EXPECT_EQ(eviction_strategy.AddKey("key2"), ErrorCode::OK);
53+
54+
// Evict a key
55+
std::string evicted_key = eviction_strategy.EvictKey();
56+
EXPECT_NE(evicted_key, "");
57+
58+
// Check if the evicted key is equal to the expected key
59+
EXPECT_EQ(evicted_key, "key1");
60+
61+
// Now we add more keys and update some of them
62+
EXPECT_EQ(eviction_strategy.AddKey("key3"), ErrorCode::OK);
63+
EXPECT_EQ(eviction_strategy.AddKey("key4"), ErrorCode::OK);
64+
EXPECT_EQ(eviction_strategy.UpdateKey("key2"), ErrorCode::OK);
65+
EXPECT_EQ(eviction_strategy.UpdateKey("key3"), ErrorCode::OK);
66+
67+
// Evict another key
68+
evicted_key = eviction_strategy.EvictKey();
69+
EXPECT_NE(evicted_key, "");
70+
// Check if the evicted key is equal to the expected key
71+
EXPECT_EQ(evicted_key, "key4");
72+
// Check size
73+
EXPECT_EQ(eviction_strategy.GetSize(), 2);
74+
// Clean up
75+
eviction_strategy.CleanUp();
76+
}
77+
78+
// Test FIFOEvictionStrategy AddKey and RemoveKey functionality
79+
TEST_F(EvictionStrategyTest, FIFOAddAndRemoveKey) {
80+
FIFOEvictionStrategy eviction_strategy;
81+
82+
// Add keys
83+
EXPECT_EQ(eviction_strategy.AddKey("key1"), ErrorCode::OK);
84+
EXPECT_EQ(eviction_strategy.AddKey("key2"), ErrorCode::OK);
85+
86+
// Check size
87+
EXPECT_EQ(eviction_strategy.GetSize(), 2);
88+
89+
// Remove a key
90+
EXPECT_EQ(eviction_strategy.RemoveKey("key1"), ErrorCode::OK); // FIFO not support remove a randomly accessed key
91+
EXPECT_EQ(eviction_strategy.GetSize(), 2);
92+
93+
// Clean up
94+
eviction_strategy.CleanUp();
95+
}
96+
97+
// Test FIFOEvictionStrategy EvictKey functionality
98+
TEST_F(EvictionStrategyTest, FIFOEvictKey) {
99+
FIFOEvictionStrategy eviction_strategy;
100+
101+
// Add keys
102+
EXPECT_EQ(eviction_strategy.AddKey("key1"), ErrorCode::OK);
103+
EXPECT_EQ(eviction_strategy.AddKey("key2"), ErrorCode::OK);
104+
105+
// Evict a key
106+
std::string evicted_key = eviction_strategy.EvictKey();
107+
EXPECT_NE(evicted_key, "");
108+
109+
// Check if the evicted key is equal to the expected key
110+
EXPECT_EQ(evicted_key, "key1");
111+
112+
// Now we add more keys and update some of them
113+
EXPECT_EQ(eviction_strategy.AddKey("key3"), ErrorCode::OK);
114+
EXPECT_EQ(eviction_strategy.AddKey("key4"), ErrorCode::OK);
115+
EXPECT_EQ(eviction_strategy.UpdateKey("key2"), ErrorCode::OK);
116+
EXPECT_EQ(eviction_strategy.UpdateKey("key3"), ErrorCode::OK);
117+
118+
// Evict another key
119+
evicted_key = eviction_strategy.EvictKey();
120+
EXPECT_NE(evicted_key, "");
121+
// Check if the evicted key is equal to the expected key
122+
EXPECT_EQ(evicted_key, "key2");
123+
// Check size
124+
EXPECT_EQ(eviction_strategy.GetSize(), 2);
125+
// Clean up
126+
eviction_strategy.CleanUp();
127+
}
128+
129+
} // namespace mooncake
130+
131+
int main(int argc, char** argv) {
132+
::testing::InitGoogleTest(&argc, argv);
133+
return RUN_ALL_TESTS();
134+
}

mooncake-transfer-engine/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ if (NOT GLOBAL_CONFIG)
55
if (USE_ETCD)
66
message(FATAL_ERROR "Cannot enable USE_ETCD while building transfer engine independently")
77
endif()
8-
98
include(../mooncake-common/common.cmake)
109
endif() # GLOBAL_CONFIG
1110

0 commit comments

Comments
 (0)