1
1
#include < homestore/btree/btree_req.hpp>
2
2
#include < homestore/btree/btree_kv.hpp>
3
+ #include < homestore/meta_service.hpp>
3
4
4
5
#include " hs_homeobject.hpp"
5
6
namespace homeobject {
6
7
7
8
/* GCManager */
8
9
9
10
GCManager::GCManager (std::shared_ptr< HeapChunkSelector > chunk_selector, HSHomeObject* homeobject) :
10
- m_chunk_selector{chunk_selector}, m_hs_home_object{homeobject} {}
11
+ m_chunk_selector{chunk_selector}, m_hs_home_object{homeobject} {
12
+ homestore::HomeStore::instance ()->meta_service ().register_handler (
13
+ GCManager::_gc_actor_meta_name,
14
+ [this ](homestore::meta_blk* mblk, sisl::byte_view buf, size_t size) {
15
+ on_gc_actor_meta_blk_found (std::move (buf), voidptr_cast (mblk));
16
+ },
17
+ nullptr , true );
18
+
19
+ homestore::HomeStore::instance ()->meta_service ().register_handler (
20
+ GCManager::_gc_reserved_chunk_meta_name,
21
+ [this ](homestore::meta_blk* mblk, sisl::byte_view buf, size_t size) {
22
+ on_reserved_chunk_meta_blk_found (std::move (buf), voidptr_cast (mblk));
23
+ },
24
+ nullptr ,
25
+ // we need to recover reserved chunk before after we recover gc actor
26
+ true , std::optional< homestore::meta_subtype_vec_t >({GCManager::_gc_actor_meta_name}));
27
+
28
+ homestore::HomeStore::instance ()->meta_service ().register_handler (
29
+ GCManager::_gc_task_meta_name,
30
+ [this ](homestore::meta_blk* mblk, sisl::byte_view buf, size_t size) {
31
+ on_gc_task_meta_blk_found (std::move (buf), voidptr_cast (mblk));
32
+ },
33
+ nullptr , true , std::optional< homestore::meta_subtype_vec_t >({GCManager::_gc_reserved_chunk_meta_name}));
34
+ }
35
+
36
+ void GCManager::on_gc_task_meta_blk_found (sisl::byte_view const & buf, void * meta_cookie) {
37
+ homestore::superblk< GCManager::gc_task_superblk > gc_task_sb (GCManager::_gc_task_meta_name);
38
+ const auto pending_gc_task = gc_task_sb.load (buf, meta_cookie);
39
+
40
+ // if a gc_task_super blk is found, we can make sure that all the valid data in move_from_chunk has been copied to
41
+ // move_to_chunk, and all the blob -> (new pba) have been written to the gc index table. Now, what we need to do is
42
+ // just updating blob indexes in pg index table according to the blob indexes in gc index table.
43
+
44
+ // pg_index_table: [pg_id, shard_id, blob_id] -> old pba
45
+ // gc_index_table: [move_to_chunk_id, pg_id, shard_id, blob_id] -> new pba
46
+
47
+ // we need to find all keys with the prefix of move_to_chunk_id in gc index table, and update the corrsponding
48
+ // keys(same pg_id + shard_id + blob_id) in pg index table with the new pba.
49
+ auto pdev_id = m_chunk_selector->get_extend_vchunk (pending_gc_task->move_from_chunk )->get_pdev_id ();
50
+ auto gc_actor = get_pdev_gc_actor (pdev_id);
51
+ RELEASE_ASSERT (gc_actor, " can not get gc actor for pdev {}!" , pdev_id);
52
+ gc_actor->handle_recovered_gc_task (pending_gc_task);
53
+
54
+ // delete gc task meta blk, so that it will not be recovered again if restart
55
+ gc_task_sb.destroy ();
56
+ }
57
+
58
+ void GCManager::on_gc_actor_meta_blk_found (sisl::byte_view const & buf, void * meta_cookie) {
59
+ homestore::superblk< GCManager::gc_actor_superblk > gc_actor_sb (GCManager::_gc_actor_meta_name);
60
+ gc_actor_sb.load (buf, meta_cookie);
61
+ auto pdev_id = gc_actor_sb->pdev_id ;
62
+ auto index_table_uuid = gc_actor_sb->index_table_uuid ;
63
+
64
+ auto gc_index_table = m_hs_home_object->get_gc_index_table (boost::uuids::to_string (index_table_uuid));
65
+
66
+ RELEASE_ASSERT (gc_index_table, " can not get gc index table for pdev {} with uuid {}!" , pdev_id,
67
+ boost::uuids::to_string (index_table_uuid));
68
+
69
+ // create a gc actor for this pdev if not exists
70
+ auto gc_actor = try_create_pdev_gc_actor (pdev_id, gc_index_table);
71
+ RELEASE_ASSERT (gc_actor, " can not get gc actor for pdev {}!" , pdev_id);
72
+ }
73
+
74
+ void GCManager::on_reserved_chunk_meta_blk_found (sisl::byte_view const & buf, void * meta_cookie) {
75
+ homestore::superblk< GCManager::gc_reserved_chunk_superblk > reserved_chunk_sb (
76
+ GCManager::_gc_reserved_chunk_meta_name);
77
+ reserved_chunk_sb.load (buf, meta_cookie);
78
+ auto chunk_id = reserved_chunk_sb->chunk_id ;
79
+ auto pdev_id = m_chunk_selector->get_extend_vchunk (chunk_id)->get_pdev_id ();
80
+ auto gc_actor = get_pdev_gc_actor (pdev_id);
81
+ RELEASE_ASSERT (gc_actor, " can not get gc actor for pdev {}!" , pdev_id);
82
+ gc_actor->add_reserved_chunk (chunk_id);
83
+ // mark a reserved chunk as gc state, so that it will not be selected as a gc candidate
84
+ m_chunk_selector->try_mark_chunk_to_gc_state (chunk_id, true /* force */ );
85
+ }
11
86
12
87
GCManager::~GCManager () { stop (); }
13
88
14
89
void GCManager::start () {
15
90
for (const auto & [pdev_id, gc_actor] : m_pdev_gc_actors) {
16
91
gc_actor->start ();
17
- LOGERROR (" start gc actor for pdev={}" , pdev_id);
92
+ LOGINFO (" start gc actor for pdev={}" , pdev_id);
18
93
}
19
94
20
95
m_gc_timer_hdl = iomanager.schedule_global_timer (
@@ -72,12 +147,22 @@ std::shared_ptr< GCManager::pdev_gc_actor > GCManager::get_pdev_gc_actor(uint32_
72
147
return it->second ;
73
148
}
74
149
75
- bool GCManager::is_eligible_for_gc (std::shared_ptr< HeapChunkSelector::ExtendedVChunk > chunk) {
150
+ bool GCManager::is_eligible_for_gc (chunk_id_t chunk_id) {
151
+ auto chunk = m_chunk_selector->get_extend_vchunk (chunk_id);
152
+
76
153
// 1 if the chunk state is inuse, it is occupied by a open shard, so it can not be selected and we don't need gc it.
77
154
// 2 if the chunk state is gc, it means this chunk is being gc, or this is a reserved chunk, so we don't need gc it.
78
- if (chunk->m_state != ChunkState::AVAILABLE) return false ;
155
+ if (chunk->m_state != ChunkState::AVAILABLE) {
156
+ LOGINFO (" chunk_id={} state is {}, not eligible for gc" , chunk_id, chunk->m_state )
157
+ return false ;
158
+ }
79
159
// it does not belong to any pg, so we don't need to gc it.
80
- if (!chunk->m_pg_id .has_value ()) return false ;
160
+ if (!chunk->m_pg_id .has_value ()) {
161
+ LOGINFO (" chunk_id={} belongs to no pg, not eligible for gc" , chunk_id)
162
+ return false ;
163
+ }
164
+
165
+ LOGINFO (" chunk_id={} is eligible for gc, belongs to pg {}" , chunk_id, chunk->m_pg_id .value ());
81
166
82
167
auto defrag_blk_num = chunk->get_defrag_nblks ();
83
168
auto total_blk_num = chunk->get_total_blks ();
@@ -88,21 +173,21 @@ bool GCManager::is_eligible_for_gc(std::shared_ptr< HeapChunkSelector::ExtendedV
88
173
}
89
174
90
175
void GCManager::scan_chunks_for_gc () {
91
- // in every iteration, we will select at most 2 * RESERVED_CHUNK_NUM_PER_PDEV gc tasks
92
- auto max_task_num = 2 * ( RESERVED_CHUNK_NUM_PER_PDEV - RESERVED_CHUNK_NUM_DEDICATED_FOR_EGC);
93
-
94
- for ( const auto & [_, chunk] : m_chunk_selector-> get_all_chunks ()) {
95
- if ( is_eligible_for_gc (chunk)) {
96
- auto pdev_id = chunk-> get_pdev_id ( );
97
- auto it = m_pdev_gc_actors. find (pdev_id) ;
98
- if (it != m_pdev_gc_actors. end ()) {
99
- auto & actor = it-> second ;
100
- auto chunk_id = chunk-> get_chunk_id ();
176
+ for ( const auto & [pdev_id, chunks] : m_chunk_selector-> get_pdev_chunks ()) {
177
+ // in every iteration, we will select at most 2 * RESERVED_CHUNK_NUM_PER_PDEV gc tasks
178
+ auto max_task_num = 2 * (RESERVED_CHUNK_NUM_PER_PDEV - RESERVED_CHUNK_NUM_DEDICATED_FOR_EGC);
179
+ auto it = m_pdev_gc_actors. find (pdev_id);
180
+ RELEASE_ASSERT (it != m_pdev_gc_actors. end (), " can not find gc actor for pdev_id {} when scanning chunks for gc " ,
181
+ pdev_id );
182
+ auto & actor = it-> second ;
183
+
184
+ for ( const auto & chunk_id : chunks) {
185
+ if ( is_eligible_for_gc (chunk_id)) {
101
186
auto future = actor->add_gc_task (static_cast < uint8_t >(task_priority::normal ), chunk_id);
102
187
if (future.isReady ()) {
103
188
// future is not expected to be ready immediately. if it is ready here, it probably means failing to
104
189
// add gc task. then we try to add one more.
105
- LOGWARN (" failed to add gc task for chunk_id={} on pdev_id={}, return value= {}" , chunk_id, pdev_id,
190
+ LOGWARN (" failed to add gc task for chunk_id={} on pdev_id={}, return value={}" , chunk_id, pdev_id,
106
191
future.value ());
107
192
} else if (0 == --max_task_num) {
108
193
LOGINFO (" reached max gc task limit for pdev_id={}, stopping further gc task submissions" , pdev_id);
@@ -168,12 +253,12 @@ folly::SemiFuture< bool > GCManager::pdev_gc_actor::add_gc_task(uint8_t priority
168
253
169
254
if (sisl_unlikely (priority == static_cast < uint8_t >(task_priority::emergent))) {
170
255
m_egc_executor->add ([this , priority, move_from_chunk, promise = std::move (promise)]() mutable {
171
- LOGERROR (" start gc task : move_from_chunk_id={}, priority={}" , move_from_chunk, priority);
256
+ LOGINFO (" start emergent gc task : move_from_chunk_id={}, priority={}" , move_from_chunk, priority);
172
257
process_gc_task (move_from_chunk, priority, std::move (promise));
173
258
});
174
259
} else {
175
260
m_gc_executor->add ([this , priority, move_from_chunk, promise = std::move (promise)]() mutable {
176
- LOGERROR (" start gc task : move_from_chunk_id={}, priority={}" , move_from_chunk, priority);
261
+ LOGINFO (" start gc task : move_from_chunk_id={}, priority={}" , move_from_chunk, priority);
177
262
process_gc_task (move_from_chunk, priority, std::move (promise));
178
263
});
179
264
}
@@ -256,8 +341,8 @@ bool GCManager::pdev_gc_actor::copy_valid_data(chunk_id_t move_from_chunk, chunk
256
341
257
342
void GCManager::pdev_gc_actor::purge_reserved_chunk (chunk_id_t chunk) {
258
343
auto vchunk = m_chunk_selector->get_extend_vchunk (chunk);
259
- RELEASE_ASSERT (!vchunk->m_pg_id .has_value (), " chunk_id={} is expected to a reserved chunk, and not belong to a pg " ,
260
- chunk);
344
+ RELEASE_ASSERT (!vchunk->m_pg_id .has_value (),
345
+ " chunk_id={} is expected to be a reserved chunk, and not belong to a pg " , chunk);
261
346
vchunk->reset (); // reset the chunk to make sure it is empty
262
347
263
348
// clear all the entries of this chunk in the gc index table
@@ -277,22 +362,8 @@ void GCManager::pdev_gc_actor::purge_reserved_chunk(chunk_id_t chunk) {
277
362
278
363
void GCManager::pdev_gc_actor::process_gc_task (chunk_id_t move_from_chunk, uint8_t priority,
279
364
folly::Promise< bool > task) {
280
- auto move_from_vchunk = m_chunk_selector->get_extend_vchunk (move_from_chunk);
281
-
282
365
LOGINFO (" start process gc task for move_from_chunk={} with priority={} " , move_from_chunk, priority);
283
366
284
- // we need to select the move_from_chunk out of the per pg chunk heap in chunk selector if it is a gc task with
285
- // normal priority. for the task with emergent priority, it is already selected since it is now used for an open
286
- // shard.
287
-
288
- if (!GCManager::is_eligible_for_gc (move_from_vchunk)) {
289
- LOGWARN (" move_from_chunk={} is not eligible for gc, so we can not process the gc task" , move_from_chunk);
290
- task.setValue (false );
291
- return ;
292
- }
293
-
294
- LOGINFO (" move_from_chunk={} belongs to pg {} " , move_from_chunk, move_from_vchunk->m_pg_id .value ());
295
-
296
367
// make chunk to gc state, so that it can be select for creating shard
297
368
auto succeed = m_chunk_selector->try_mark_chunk_to_gc_state (
298
369
move_from_chunk, priority == static_cast < uint8_t >(task_priority::emergent) /* force */ );
@@ -353,7 +424,7 @@ void GCManager::pdev_gc_actor::process_gc_task(chunk_id_t move_from_chunk, uint8
353
424
354
425
GCManager::pdev_gc_actor::~pdev_gc_actor () {
355
426
stop ();
356
- LOGINFO (" pdev gc actor for pdev_id={} is destroyed" , m_pdev_id);
427
+ LOGINFO (" gc actor for pdev_id={} is destroyed" , m_pdev_id);
357
428
}
358
429
359
430
/* RateLimiter */
0 commit comments