Skip to content

Commit c3cc258

Browse files
committed
implement gc data copy
1 parent e79274f commit c3cc258

20 files changed

+1302
-141
lines changed

conanfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
class HomeObjectConan(ConanFile):
1111
name = "homeobject"
12-
version = "2.3.23"
12+
version = "2.3.24"
1313

1414
homepage = "https://github.com/eBay/HomeObject"
1515
description = "Blob Store built on HomeReplication"

src/include/homeobject/shard_manager.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ struct ShardInfo {
2727
uint64_t last_modified_time;
2828
uint64_t available_capacity_bytes;
2929
uint64_t total_capacity_bytes;
30-
uint64_t deleted_capacity_bytes;
3130
std::optional< peer_id_t > current_leader{std::nullopt};
3231

3332
auto operator<=>(ShardInfo const& rhs) const { return id <=> rhs.id; }

src/lib/homeobject_impl.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,8 @@ class HomeObjectImpl : public HomeObject,
161161

162162
/// BlobManager
163163
BlobManager::AsyncResult< blob_id_t > put(shard_id_t shard, Blob&&, trace_id_t tid) final;
164-
BlobManager::AsyncResult< Blob > get(shard_id_t shard, blob_id_t const& blob, uint64_t off,
165-
uint64_t len, trace_id_t tid) const final;
164+
BlobManager::AsyncResult< Blob > get(shard_id_t shard, blob_id_t const& blob, uint64_t off, uint64_t len,
165+
trace_id_t tid) const final;
166166
BlobManager::NullAsyncResult del(shard_id_t shard, blob_id_t const& blob, trace_id_t tid) final;
167167
};
168168

src/lib/homestore_backend/CMakeLists.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ add_test(NAME HomestoreResyncTestWithLeaderRestart
119119
add_executable(homestore_test_gc)
120120
target_sources(homestore_test_gc PRIVATE $<TARGET_OBJECTS:homestore_tests_gc>)
121121
target_link_libraries(homestore_test_gc PUBLIC homeobject_homestore ${COMMON_TEST_DEPS})
122-
add_test(NAME HomestoreTestGC COMMAND homestore_test_pg -csv error --executor immediate --config_path ./
123-
--override_config homestore_config.consensus.snapshot_freq_distance:0
124-
--override_config homestore_config.consensus.max_grpc_message_size:138412032)
122+
add_test(NAME HomestoreTestGC COMMAND homestore_test_gc -csv error --executor immediate --config_path ./
123+
--override_config hs_backend_config.enable_gc=true
124+
--override_config hs_backend_config.gc_garbage_rate_threshold=1
125+
--override_config hs_backend_config.gc_scan_interval_sec=5)
125126

src/lib/homestore_backend/gc_manager.cpp

Lines changed: 443 additions & 40 deletions
Large diffs are not rendered by default.

src/lib/homestore_backend/gc_manager.hpp

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,11 @@
1616

1717
#include "heap_chunk_selector.h"
1818
#include "index_kv.hpp"
19+
#include "hs_backend_config.hpp"
1920

2021
namespace homeobject {
2122

2223
class HSHomeObject;
23-
// TODO:: make those #define below configurable
24-
25-
// Default number of chunks reserved for GC per pdev
26-
#define RESERVED_CHUNK_NUM_PER_PDEV 6
27-
// reserved chunk number dedicated for emergent GC
28-
#define RESERVED_CHUNK_NUM_DEDICATED_FOR_EGC 2
29-
// GC interval in seconds, this is used to schedule the GC timer
30-
#define GC_SCAN_INTERVAL_SEC 10llu
31-
// Garbage rate threshold, bigger than which, a gc task will be scheduled for a chunk
32-
#define GC_GARBAGE_RATE_THRESHOLD 80
33-
// limit the io resource that gc thread can take, so that it will not impact the client io.
34-
// assuming the throughput of a HDD is 300M/s(including read and write) and gc can take 10% of the io resource, which is
35-
// 30M/s. A block is 4K, so gc can read/write 30M/s / 4K = 7680 blocks per second.
36-
#define MAX_READ_WRITE_BLOCK_COUNT_PER_SECOND 7680
3724

3825
ENUM(task_priority, uint8_t, emergent = 0, normal, priority_count);
3926

@@ -72,7 +59,7 @@ class GCManager {
7259

7360
struct gc_reserved_chunk_superblk {
7461
chunk_id_t chunk_id;
75-
static std::string name() { return _gc_actor_meta_name; }
62+
static std::string name() { return _gc_reserved_chunk_meta_name; }
7663
};
7764
#pragma pack()
7865

@@ -114,7 +101,7 @@ class GCManager {
114101
pdev_gc_actor& operator=(pdev_gc_actor&&) = delete;
115102

116103
public:
117-
void add_reserved_chunk(chunk_id_t chunk_id);
104+
void add_reserved_chunk(homestore::superblk< GCManager::gc_reserved_chunk_superblk > reserved_chunk_sb);
118105
folly::SemiFuture< bool > add_gc_task(uint8_t priority, chunk_id_t move_from_chunk);
119106
void handle_recovered_gc_task(const GCManager::gc_task_superblk* gc_task);
120107
void start();
@@ -136,7 +123,7 @@ class GCManager {
136123
// before we select a reserved chunk and start gc, we need:
137124
// 1 clear all the entries of this chunk in the gc index table
138125
// 2 reset this chunk to make sure it is empty.
139-
void purge_reserved_chunk(chunk_id_t move_to_chunk);
126+
bool purge_reserved_chunk(chunk_id_t move_to_chunk);
140127

141128
private:
142129
// utils
@@ -148,10 +135,18 @@ class GCManager {
148135
folly::MPMCQueue< chunk_id_t > m_reserved_chunk_queue;
149136
std::shared_ptr< GCBlobIndexTable > m_index_table;
150137
HSHomeObject* m_hs_home_object{nullptr};
151-
RateLimiter m_rate_limiter{MAX_READ_WRITE_BLOCK_COUNT_PER_SECOND};
138+
139+
// limit the io resource that gc thread can take, so that it will not impact the client io.
140+
// assuming the throughput of a HDD is 300M/s(including read and write) and gc can take 10% of the io resource,
141+
// which is 30M/s. A block is 4K, so gc can read/write 30M/s / 4K = 7680 blocks per second.
142+
RateLimiter m_rate_limiter{HS_BACKEND_DYNAMIC_CONFIG(max_read_write_block_count_per_second)};
143+
152144
std::shared_ptr< folly::IOThreadPoolExecutor > m_gc_executor;
153145
std::shared_ptr< folly::IOThreadPoolExecutor > m_egc_executor;
154146
std::atomic_bool m_is_stopped{true};
147+
// since we have a very small number of reserved chunks, a vector is enough
148+
// TODO:: use a map if we have a large number of reserved chunks
149+
std::vector< homestore::superblk< GCManager::gc_reserved_chunk_superblk > > m_reserved_chunks;
155150
};
156151

157152
public:
@@ -179,9 +174,9 @@ class GCManager {
179174
void start();
180175
void stop();
181176

182-
private:
183177
void scan_chunks_for_gc();
184178

179+
private:
185180
void on_gc_task_meta_blk_found(sisl::byte_view const& buf, void* meta_cookie);
186181
void on_gc_actor_meta_blk_found(sisl::byte_view const& buf, void* meta_cookie);
187182
void on_reserved_chunk_meta_blk_found(sisl::byte_view const& buf, void* meta_cookie);

src/lib/homestore_backend/heap_chunk_selector.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,31 @@ std::optional< uint32_t > HeapChunkSelector::select_chunks_for_pg(pg_id_t pg_id,
263263
return num_chunk;
264264
}
265265

266+
void HeapChunkSelector::switch_chunks_for_pg(const pg_id_t pg_id, const chunk_num_t old_chunk_id,
267+
const chunk_num_t new_chunk_id) {
268+
auto EXVchunk_old = get_extend_vchunk(old_chunk_id);
269+
RELEASE_ASSERT(EXVchunk_old->m_v_chunk_id.has_value(), "old_chunk_id={} should has a vchunk_id", old_chunk_id);
270+
RELEASE_ASSERT(EXVchunk_old->m_pg_id.has_value(), "old_chunk_id={} should belongs to a pg", old_chunk_id);
271+
RELEASE_ASSERT(EXVchunk_old->m_pg_id.value() == pg_id, "old_chunk_id={} should belongs to pg={}", old_chunk_id,
272+
pg_id);
273+
274+
auto v_chunk_id = EXVchunk_old->m_v_chunk_id.value();
275+
276+
std::unique_lock lock_guard(m_chunk_selector_mtx);
277+
auto pg_it = m_per_pg_chunks.find(pg_id);
278+
RELEASE_ASSERT(pg_it != m_per_pg_chunks.end(), "No pg_chunk_collection found for pg={}", pg_id);
279+
auto& pg_chunk_collection = pg_it->second;
280+
auto& pg_chunks = pg_chunk_collection->m_pg_chunks;
281+
std::scoped_lock lock(pg_chunk_collection->mtx);
282+
auto old_available_blks = pg_chunks[v_chunk_id]->available_blks();
283+
pg_chunks[v_chunk_id] = get_extend_vchunk(new_chunk_id);
284+
pg_chunks[v_chunk_id]->m_pg_id = pg_id;
285+
pg_chunks[v_chunk_id]->m_v_chunk_id = v_chunk_id;
286+
auto new_available_blks = pg_chunks[v_chunk_id]->available_blks();
287+
288+
pg_chunk_collection->available_blk_count += new_available_blks - old_available_blks;
289+
}
290+
266291
bool HeapChunkSelector::recover_pg_chunks(pg_id_t pg_id, std::vector< chunk_num_t >&& p_chunk_ids) {
267292
std::unique_lock lock_guard(m_chunk_selector_mtx);
268293
// check pg exist

src/lib/homestore_backend/heap_chunk_selector.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class HeapChunkSelector : public homestore::ChunkSelector {
128128
// this should be called on each pg meta blk found
129129
bool recover_pg_chunks(pg_id_t pg_id, std::vector< chunk_num_t >&& p_chunk_ids);
130130

131-
// this should be called after all pg meta blk recovered
131+
// this should be called after all pg meta blk and gc reserved chunk recovered
132132
void build_pdev_available_chunk_heap();
133133

134134
// this should be called after ShardManager is initialized and get all the open shards
@@ -198,6 +198,8 @@ class HeapChunkSelector : public homestore::ChunkSelector {
198198

199199
homestore::cshared< ExtendedVChunk > get_extend_vchunk(const chunk_num_t chunk_id) const;
200200

201+
void switch_chunks_for_pg(const pg_id_t pg_id, const chunk_num_t old_chunk_id, const chunk_num_t new_chunk_id);
202+
201203
private:
202204
void add_chunk_internal(const chunk_num_t, bool add_to_heap = true);
203205

src/lib/homestore_backend/hs_backend_config.fbs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,26 @@ table HSBackendSettings {
1818

1919
//Reserved space in a chunk
2020
reserved_bytes_in_chunk: uint64 = 16777216 (hotswap);
21+
22+
//Enable GC
23+
//TODO: make this hotswap after gc is well tested
24+
enable_gc: bool = false;
25+
26+
//Total reserved chunk num (dedicated for gc/egc) per pdev
27+
reserved_chunk_num_per_pdev: uint8 = 6;
28+
29+
//Reserved chunk number (dedicated for egc) per pdev
30+
reserved_chunk_num_per_pdev_for_egc: uint8 = 2;
31+
32+
//GC scan interval(second)
33+
gc_scan_interval_sec: uint64 = 60;
34+
35+
//GC garbage rate threshold, upon which a chunk will be selected for gc
36+
gc_garbage_rate_threshold: uint8 = 80;
37+
38+
//max read/write block count per second, which is used by ratelimiter to limit the io resource taken by gc
39+
max_read_write_block_count_per_second: uint16 = 7680;
40+
2141
}
2242

2343
root_type HSBackendSettings;

src/lib/homestore_backend/hs_homeobject.cpp

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,11 @@ void HSHomeObject::init_homestore() {
223223
repl_app->on_repl_devs_init_completed();
224224

225225
// we need to regitster the meta blk handlers after metaservice is ready.
226-
gc_mgr_ = std::make_unique< GCManager >(chunk_selector_, this);
226+
gc_mgr_ = std::make_shared< GCManager >(chunk_selector_, this);
227227
// if this is the first time we are starting, we need to create gc metablk for each pdev, which record the
228228
// reserved chunks and indextable.
229229
auto pdev_chunks = chunk_selector_->get_pdev_chunks();
230+
const auto reserved_chunk_num_per_pdev = HS_BACKEND_DYNAMIC_CONFIG(reserved_chunk_num_per_pdev);
230231
for (auto const& [pdev_id, chunks] : pdev_chunks) {
231232
// 1 create gc index table for each pdev
232233
auto gc_index_table = create_gc_index_table();
@@ -241,12 +242,12 @@ void HSHomeObject::init_homestore() {
241242
gc_actor_sb->index_table_uuid = uuid;
242243
gc_actor_sb.write();
243244

244-
RELEASE_ASSERT(chunks.size() > RESERVED_CHUNK_NUM_PER_PDEV,
245+
RELEASE_ASSERT(chunks.size() > reserved_chunk_num_per_pdev,
245246
"pdev {} has {} chunks, but we need at least {} chunks for reserved chunk", pdev_id,
246-
chunks.size(), RESERVED_CHUNK_NUM_PER_PDEV);
247+
chunks.size(), reserved_chunk_num_per_pdev);
247248

248249
// 3 create reserved chunk meta blk for each pdev, which contains the reserved chunks.
249-
for (size_t i = 0; i < RESERVED_CHUNK_NUM_PER_PDEV; ++i) {
250+
for (size_t i = 0; i < reserved_chunk_num_per_pdev; ++i) {
250251
auto chunk = chunks[i];
251252
homestore::superblk< GCManager::gc_reserved_chunk_superblk > reserved_chunk_sb{
252253
GCManager::_gc_reserved_chunk_meta_name};
@@ -352,15 +353,21 @@ void HSHomeObject::init_cp() {
352353

353354
void HSHomeObject::init_gc() {
354355
using namespace homestore;
355-
if (!gc_mgr_) gc_mgr_ = std::make_unique< GCManager >(chunk_selector_, this);
356+
if (!gc_mgr_) gc_mgr_ = std::make_shared< GCManager >(chunk_selector_, this);
356357
// when initializing, there is not gc task. we need to recover reserved chunks here, so that the reserved chunks
357358
// will not be put into pdev heap when built
358359
HomeStore::instance()->meta_service().read_sub_sb(GCManager::_gc_actor_meta_name);
359360
HomeStore::instance()->meta_service().read_sub_sb(GCManager::_gc_reserved_chunk_meta_name);
360361
HomeStore::instance()->meta_service().read_sub_sb(GCManager::_gc_task_meta_name);
361362

362-
// TODO::enable gc after we have data copy
363-
// gc_mgr_->start();
363+
const auto enable_gc = HS_BACKEND_DYNAMIC_CONFIG(enable_gc);
364+
365+
if (enable_gc) {
366+
LOGI("Starting GC");
367+
gc_mgr_->start();
368+
} else {
369+
LOGI("GC is disabled");
370+
}
364371
}
365372

366373
// void HSHomeObject::trigger_timed_events() { persist_pg_sb(); }
@@ -452,4 +459,13 @@ std::shared_ptr< GCBlobIndexTable > HSHomeObject::get_gc_index_table(std::string
452459
return it->second;
453460
}
454461

462+
void HSHomeObject::trigger_immediate_gc() {
463+
if (!gc_mgr_) {
464+
LOGI("scan chunks for gc immediately");
465+
gc_mgr_->scan_chunks_for_gc();
466+
} else {
467+
LOGE("GC is not enabled");
468+
}
469+
}
470+
455471
} // namespace homeobject

src/lib/homestore_backend/hs_homeobject.hpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class HSHomeObject : public HomeObjectImpl {
8282
std::once_flag replica_restart_flag_;
8383

8484
// mapping from chunk to shard list.
85-
folly::ConcurrentHashMap< homestore::chunk_num_t, std::set< shard_id_t > > chunk_to_shards_map_;
85+
std::unordered_map< homestore::chunk_num_t, std::set< shard_id_t > > chunk_to_shards_map_;
8686

8787
public:
8888
#pragma pack(1)
@@ -347,7 +347,7 @@ class HSHomeObject : public HomeObjectImpl {
347347
HS_Shard(homestore::superblk< shard_info_superblk >&& sb);
348348
~HS_Shard() override = default;
349349

350-
void update_info(const ShardInfo& info);
350+
void update_info(const ShardInfo& info, std::optional< homestore::chunk_num_t > p_chunk_id = std::nullopt);
351351
auto p_chunk_id() const { return sb_->p_chunk_id; }
352352
};
353353

@@ -622,7 +622,7 @@ class HSHomeObject : public HomeObjectImpl {
622622
std::unordered_map< homestore::group_id_t, homestore::superblk< snapshot_ctx_superblk > > snp_ctx_sbs_;
623623
mutable std::shared_mutex snp_sbs_lock_;
624624
shared< HeapChunkSelector > chunk_selector_;
625-
std::unique_ptr< GCManager > gc_mgr_;
625+
shared< GCManager > gc_mgr_;
626626
unique< HttpManager > http_mgr_;
627627
bool recovery_done_{false};
628628

@@ -766,6 +766,9 @@ class HSHomeObject : public HomeObjectImpl {
766766
*/
767767
std::optional< homestore::chunk_num_t > get_shard_p_chunk_id(shard_id_t id) const;
768768

769+
void update_shard_meta_after_gc(const homestore::chunk_num_t move_from_chunk,
770+
const homestore::chunk_num_t move_to_chunk);
771+
769772
/**
770773
* @brief Retrieves the chunk number associated with the given shard ID.
771774
*
@@ -818,6 +821,7 @@ class HSHomeObject : public HomeObjectImpl {
818821
cintrusive< homestore::repl_req_ctx >& hs_ctx);
819822

820823
cshared< HeapChunkSelector > chunk_selector() const { return chunk_selector_; }
824+
cshared< GCManager > gc_manager() const { return gc_mgr_; }
821825

822826
// Blob manager related.
823827
void on_blob_message_rollback(int64_t lsn, sisl::blob const& header, sisl::blob const& key,
@@ -837,14 +841,20 @@ class HSHomeObject : public HomeObjectImpl {
837841
recover_index_table(homestore::superblk< homestore::index_table_sb >&& sb);
838842
std::optional< pg_id_t > get_pg_id_with_group_id(homestore::group_id_t group_id) const;
839843

840-
const auto get_shards_in_chunk(homestore::chunk_num_t chunk_id) const { return chunk_to_shards_map_.at(chunk_id); }
844+
const std::set< shard_id_t > get_shards_in_chunk(homestore::chunk_num_t chunk_id) const;
845+
846+
void update_pg_meta_after_gc(const pg_id_t pg_id, const homestore::chunk_num_t move_from_chunk,
847+
const homestore::chunk_num_t move_to_chunk);
848+
849+
uint32_t get_pg_tombstone_blob_count(pg_id_t pg_id) const;
841850

842851
// Snapshot persistence related
843852
sisl::io_blob_safe get_snapshot_sb_data(homestore::group_id_t group_id);
844853
void update_snapshot_sb(homestore::group_id_t group_id, std::shared_ptr< homestore::snapshot_context > ctx);
845854
void destroy_snapshot_sb(homestore::group_id_t group_id);
846855
const Shard* _get_hs_shard(const shard_id_t shard_id) const;
847856
std::shared_ptr< GCBlobIndexTable > get_gc_index_table(std::string uuid) const;
857+
void trigger_immediate_gc();
848858

849859
private:
850860
std::shared_ptr< BlobIndexTable > create_pg_index_table();

0 commit comments

Comments
 (0)