From 9b264a212f1245dcc358b893d22bf5d15cd8806f Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Fri, 3 Apr 2026 17:00:06 +0000 Subject: [PATCH] Do full metadata GC on start (new store) --- src/blockstore/blockstore_disk.cpp | 2 + src/blockstore/blockstore_disk.h | 2 + src/blockstore/blockstore_heap.cpp | 120 ++++++++++++++++++++--------- src/blockstore/blockstore_heap.h | 2 + src/test/test_heap.cpp | 1 + 5 files changed, 92 insertions(+), 35 deletions(-) diff --git a/src/blockstore/blockstore_disk.cpp b/src/blockstore/blockstore_disk.cpp index 8152264f..df1f3bd6 100644 --- a/src/blockstore/blockstore_disk.cpp +++ b/src/blockstore/blockstore_disk.cpp @@ -94,6 +94,8 @@ void blockstore_disk_t::parse_config(std::map & config csum_block_size = parse_size(config["csum_block_size"]); discard_on_start = config.find("discard_on_start") != config.end() && (config["discard_on_start"] == "true" || config["discard_on_start"] == "1" || config["discard_on_start"] == "yes"); + gc_on_start = config.find("gc_on_start") == config.end() || + (config["gc_on_start"] == "true" || config["gc_on_start"] == "1" || config["gc_on_start"] == "yes"); min_discard_size = parse_size(config["min_discard_size"]); if (!min_discard_size) min_discard_size = 1024*1024; diff --git a/src/blockstore/blockstore_disk.h b/src/blockstore/blockstore_disk.h index c251aa8f..ba67f0ac 100644 --- a/src/blockstore/blockstore_disk.h +++ b/src/blockstore/blockstore_disk.h @@ -57,6 +57,8 @@ struct blockstore_disk_t bool inmemory_journal = true; // Data discard granularity and minimum size (for the sake of performance) bool discard_on_start = false; + // GC on start (new store) + bool gc_on_start = true; uint64_t min_discard_size = 1024*1024; uint64_t discard_granularity = 0; diff --git a/src/blockstore/blockstore_heap.cpp b/src/blockstore/blockstore_heap.cpp index a4f2f569..3fe5ec7a 100644 --- a/src/blockstore/blockstore_heap.cpp +++ b/src/blockstore/blockstore_heap.cpp @@ -673,9 +673,53 @@ int blockstore_heap_t::mark_used_blocks() }); } } + if (dsk->gc_on_start) + { + recheck_full_gc(); + } return res; } +void blockstore_heap_t::recheck_full_gc() +{ + uint32_t block_num = 0; + for (auto & inf: block_info) + { + // Instantly collect all garbage on restart + if (inf.garbage_space > 0) + { + if (log_level > 5) + { + fprintf(stderr, "Clearing %u out of %u garbage bytes in block %u\n", inf.garbage_space, inf.used_space, block_num); + } + uint32_t collected_garbage = 0; + size_t i = 0, j = 0; + for (; i < inf.entries.size(); i++) + { + if (inf.entries[i]->entry.is_garbage()) + { + collected_garbage += inf.entries[i]->entry.size; + remove_list_item(inf.entries[i]); + } + else + { + if (j != i) + inf.entries[j] = inf.entries[i]; + j++; + } + } + inf.entries.resize(j); + modify_alloc(block_num, [&](heap_block_info_t & inf) + { + inf.used_space -= collected_garbage; + inf.garbage_space -= collected_garbage; + }); + recheck_modified_blocks.insert(block_num); + } + block_num++; + } +} + void blockstore_heap_t::recheck_buffer(heap_entry_t *cwr, uint8_t *buf) { auto free_entry = [&](heap_list_item_t *li) @@ -2336,44 +2380,50 @@ void blockstore_heap_t::apply_inflight(heap_inflight_lsn_t & inflight) { // Remove entry auto li = list_item(wr); - auto prev = li->prev; - auto next = li->next; - if (prev) - { - prev->next = next; - } - if (!next) - { - // The last freed entry must be a deletion - assert(!prev); - assert(wr->entry_type == BS_HEAP_DELETE|BS_HEAP_STABLE); - auto & pg_idx = block_index[get_pg_id(wr->inode, wr->stripe)]; - auto & inode_idx = pg_idx[wr->inode]; - heap_inode_map_t::iterator li_it; - heap_list_item_t *old_li = NULL; - inode_map_get(inode_idx, li_it, old_li, wr->stripe); - inode_map_erase(pg_idx, inode_idx, li_it, old_li); - } - else - { - next->prev = prev; - if (!prev && next->entry.entry_type == (BS_HEAP_DELETE|BS_HEAP_STABLE)) - { - // free BS_HEAP_DELETEs when all previous entries are also freed - mark_garbage(next->block_num, &next->entry, UINT32_MAX); - } - } - if (li->entry.is_garbage()) - { - garbage_entries--; - garbage_memory -= list_item_overhead(li->entry.size); - } - live_entries--; - live_memory -= list_item_overhead(li->entry.size); - free(li); + remove_list_item(li); } } +void blockstore_heap_t::remove_list_item(heap_list_item_t *li) +{ + auto prev = li->prev; + auto next = li->next; + if (prev) + { + prev->next = next; + } + if (!next) + { + // The last freed entry must be a deletion + assert(!prev); + auto wr = &li->entry; + assert(wr->entry_type == BS_HEAP_DELETE|BS_HEAP_STABLE); + auto & pg_idx = block_index[get_pg_id(wr->inode, wr->stripe)]; + auto & inode_idx = pg_idx[wr->inode]; + heap_inode_map_t::iterator li_it; + heap_list_item_t *old_li = NULL; + inode_map_get(inode_idx, li_it, old_li, wr->stripe); + inode_map_erase(pg_idx, inode_idx, li_it, old_li); + } + else + { + next->prev = prev; + if (!prev && next->entry.entry_type == (BS_HEAP_DELETE|BS_HEAP_STABLE)) + { + // free BS_HEAP_DELETEs when all previous entries are also freed + mark_garbage(next->block_num, &next->entry, UINT32_MAX); + } + } + if (li->entry.is_garbage()) + { + garbage_entries--; + garbage_memory -= list_item_overhead(li->entry.size); + } + live_entries--; + live_memory -= list_item_overhead(li->entry.size); + free(li); +} + bool blockstore_heap_t::is_lsn_completed(uint64_t lsn) { if (lsn <= completed_lsn) diff --git a/src/blockstore/blockstore_heap.h b/src/blockstore/blockstore_heap.h index 1f6dd036..2cc26ff3 100644 --- a/src/blockstore/blockstore_heap.h +++ b/src/blockstore/blockstore_heap.h @@ -220,6 +220,7 @@ class blockstore_heap_t bool validate_object(heap_entry_t *obj); void fill_recheck_queue(); int mark_used_blocks(); + void recheck_full_gc(); void recheck_buffer(heap_entry_t *cwr, uint8_t *buf); void defragment_block(uint32_t block_num); void reshard_add(heap_reshard_state_t *st, heap_list_item_t *li); @@ -227,6 +228,7 @@ class blockstore_heap_t void gc_block(heap_block_info_t & inf); int allocate_entry(uint32_t entry_size, uint32_t *block_num, bool allow_last_free); void insert_list_item(heap_list_item_t *li); + void remove_list_item(heap_list_item_t *li); int add_entry(uint32_t wr_size, uint32_t *modified_block, bool allow_last_free, bool explicit_complete, std::function fill_entry); int add_simple(heap_entry_t *obj, uint64_t version, uint32_t *modified_block, uint32_t entry_type); diff --git a/src/test/test_heap.cpp b/src/test/test_heap.cpp index 5e60c03a..5eed117c 100644 --- a/src/test/test_heap.cpp +++ b/src/test/test_heap.cpp @@ -1990,6 +1990,7 @@ void test_redirect_intent_csums() // reload heap to check that the write is still here { + dsk.gc_on_start = false; blockstore_heap_t heap(&dsk, buffer_area.data(), 10); uint64_t entries_loaded; heap.load_blocks(0, dsk.meta_block_size, tmp.data(), false, entries_loaded);