From 2a550d4d130edd3ce94ba40e13bcd507e8d849b6 Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Sat, 9 Aug 2025 21:55:52 +0300 Subject: [PATCH] unordered_map mvcc --- src/blockstore/blockstore_heap.cpp | 139 +++++++++++++++++------------ src/blockstore/blockstore_heap.h | 28 +++++- 2 files changed, 111 insertions(+), 56 deletions(-) diff --git a/src/blockstore/blockstore_heap.cpp b/src/blockstore/blockstore_heap.cpp index 1d50af7f..c772f7be 100644 --- a/src/blockstore/blockstore_heap.cpp +++ b/src/blockstore/blockstore_heap.cpp @@ -857,33 +857,43 @@ heap_object_t *blockstore_heap_t::lock_and_read_entry(object_id oid, uint64_t & { return NULL; } - auto mvcc_it = object_mvcc.lower_bound({ .oid = { .inode = oid.inode, .stripe = oid.stripe+1 }, .lsn = 0 }); - copy_id = 1; - if (mvcc_it != object_mvcc.begin()) + heap_mvcc_copy_id_t mvcc_id = { .oid = { .inode = oid.inode, .stripe = oid.stripe }, .copy_id = 1 }; + auto mvcc_it = object_mvcc.find(mvcc_id); + if (mvcc_it != object_mvcc.end()) { - mvcc_it--; - if (mvcc_it->first.oid == oid) + while (true) { - if (mvcc_it->second.entry_copy) + mvcc_id.copy_id++; + auto next_it = object_mvcc.find(mvcc_id); + if (next_it == object_mvcc.end()) { - // Already modified, need to create another copy - copy_id = mvcc_it->first.lsn+1; - } - else - { - copy_id = mvcc_it->first.lsn; - mvcc_it->second.readers++; - return obj; + mvcc_id.copy_id--; + break; } + mvcc_it = next_it; + } + if (mvcc_it->second.entry_copy) + { + // Already modified, need to create another copy + mvcc_id.copy_id++; + object_mvcc[mvcc_id] = (heap_object_mvcc_t){ .readers = 1 }; + } + else + { + mvcc_it->second.readers++; } } - object_mvcc[(heap_object_lsn_t){ .oid = oid, .lsn = copy_id }] = (heap_object_mvcc_t){ .readers = 1 }; + else + { + object_mvcc[mvcc_id] = (heap_object_mvcc_t){ .readers = 1 }; + } + copy_id = mvcc_id.copy_id; return obj; } heap_object_t *blockstore_heap_t::read_locked_entry(object_id oid, uint64_t copy_id) { - auto mvcc_it = object_mvcc.find((heap_object_lsn_t){ .oid = oid, .lsn = copy_id }); + auto mvcc_it = object_mvcc.find((heap_mvcc_copy_id_t){ .oid = oid, .copy_id = copy_id }); if (mvcc_it == object_mvcc.end()) { return NULL; @@ -895,9 +905,22 @@ heap_object_t *blockstore_heap_t::read_locked_entry(object_id oid, uint64_t copy return read_entry(oid, NULL); } +void blockstore_heap_t::free_mvcc(std::unordered_map::iterator mvcc_it) +{ + if (mvcc_it->second.entry_copy) + { + // Free refcounted data & buffer blocks + heap_object_t *obj = (heap_object_t*)mvcc_it->second.entry_copy; + free_object_space(obj->inode, obj->get_writes(), NULL, BS_HEAP_FREE_MVCC); + free(mvcc_it->second.entry_copy); + } + object_mvcc.erase(mvcc_it); +} + bool blockstore_heap_t::unlock_entry(object_id oid, uint64_t copy_id) { - auto mvcc_it = object_mvcc.find((heap_object_lsn_t){ .oid = oid, .lsn = copy_id }); + auto mvcc_id = (heap_mvcc_copy_id_t){ .oid = oid, .copy_id = copy_id }; + auto mvcc_it = object_mvcc.find(mvcc_id); if (mvcc_it == object_mvcc.end()) { return false; @@ -905,37 +928,37 @@ bool blockstore_heap_t::unlock_entry(object_id oid, uint64_t copy_id) mvcc_it->second.readers--; if (!mvcc_it->second.readers) { - if (mvcc_it->second.entry_copy) + mvcc_id.copy_id++; + if (object_mvcc.find(mvcc_id) != object_mvcc.end()) { - // Free refcounted data & buffer blocks - heap_object_t *obj = (heap_object_t*)mvcc_it->second.entry_copy; - free_object_space(obj->inode, obj->get_writes(), NULL, BS_HEAP_FREE_MVCC); - bool is_last_mvcc = true; - if (mvcc_it != object_mvcc.end()) - { - // object_mvcc may contain multiple copied entries, but always in a whole sequence - auto next_it = std::next(mvcc_it); - if (next_it->first.oid == oid && next_it->second.entry_copy) - is_last_mvcc = false; - } - if (is_last_mvcc && mvcc_it != object_mvcc.begin()) - { - auto prev_it = std::prev(mvcc_it); - if (prev_it->first.oid == oid && prev_it->second.entry_copy) - is_last_mvcc = false; - } - if (is_last_mvcc) - { - // Free data references from the newest object version when the last MVCC is freed - heap_object_t *new_obj = read_entry(oid, NULL); - if (new_obj) - { - free_object_space(new_obj->inode, new_obj->get_writes(), NULL, BS_HEAP_FREE_MAIN); - } - } - free(mvcc_it->second.entry_copy); + // Next entry isn't freed yet + return true; + } + mvcc_id.copy_id--; + // Free this entry + free_mvcc(mvcc_it); + // Free all previous entries with 0 refcount + while (mvcc_id.copy_id > 1) + { + mvcc_id.copy_id--; + mvcc_it = object_mvcc.find(mvcc_id); + assert(mvcc_it != object_mvcc.end()); + if (mvcc_it->second.readers) + { + mvcc_id.copy_id++; + break; + } + free_mvcc(mvcc_it); + } + if (mvcc_id.copy_id == 1) + { + // Free data references from the newest object version when the last MVCC is freed + heap_object_t *new_obj = read_entry(oid, NULL); + if (new_obj) + { + free_object_space(new_obj->inode, new_obj->get_writes(), NULL, BS_HEAP_FREE_MAIN); + } } - object_mvcc.erase(mvcc_it); } return true; } @@ -1306,13 +1329,13 @@ int blockstore_heap_t::add_object(object_id oid, heap_write_t *wr, uint32_t *mod bool blockstore_heap_t::mvcc_check_tracking(object_id oid) { - auto mvcc_it = object_mvcc.lower_bound({ .oid = { .inode = oid.inode, .stripe = oid.stripe }, .lsn = 0 }); + auto mvcc_it = object_mvcc.find((heap_mvcc_copy_id_t){ .oid = oid, .copy_id = 1 }); if (mvcc_it == object_mvcc.end()) { // no copies return false; } - return (mvcc_it->first.oid == oid && mvcc_it->second.entry_copy); + return mvcc_it->second.entry_copy; } void blockstore_heap_t::copy_full_object(uint8_t *dst, heap_object_t *obj) @@ -1335,17 +1358,23 @@ void blockstore_heap_t::copy_full_object(uint8_t *dst, heap_object_t *obj) bool blockstore_heap_t::mvcc_save_copy(heap_object_t *obj) { auto oid = (object_id){ .inode = obj->inode, .stripe = obj->stripe }; - auto mvcc_it = object_mvcc.lower_bound({ .oid = { .inode = oid.inode, .stripe = oid.stripe+1 }, .lsn = 0 }); - if (mvcc_it == object_mvcc.begin()) + auto mvcc_id = (heap_mvcc_copy_id_t){ .oid = oid, .copy_id = 1 }; + auto mvcc_it = object_mvcc.find(mvcc_id); + if (mvcc_it == object_mvcc.end()) { // no copies return false; } - mvcc_it--; - if (mvcc_it->first.oid != oid) + while (true) { - // no copies :-) - return false; + mvcc_id.copy_id++; + auto next_it = object_mvcc.find(mvcc_id); + if (next_it == object_mvcc.end()) + { + mvcc_id.copy_id--; + break; + } + mvcc_it = next_it; } if (mvcc_it->second.entry_copy) { @@ -1365,7 +1394,7 @@ bool blockstore_heap_t::mvcc_save_copy(heap_object_t *obj) bool for_obj = false; // save_copy is performed when the object is modified, so object_mvcc may only // contain 1 version with entry_copy == NULL - if (mvcc_it == object_mvcc.begin() || std::prev(mvcc_it)->first.oid != oid) + if (mvcc_id.copy_id == 1) { // Init refcounts for the copy and for the object itself, when it's the first MVCC entry add_ref = 2; diff --git a/src/blockstore/blockstore_heap.h b/src/blockstore/blockstore_heap.h index 94061150..e9dc3103 100644 --- a/src/blockstore/blockstore_heap.h +++ b/src/blockstore/blockstore_heap.h @@ -97,6 +97,31 @@ struct tmp_compact_item_t bool compact; }; +struct heap_mvcc_copy_id_t +{ + object_id oid; + uint64_t copy_id; +}; + +inline bool operator == (const heap_mvcc_copy_id_t & a, const heap_mvcc_copy_id_t & b) +{ + return a.oid.inode == b.oid.inode && a.oid.stripe == b.oid.stripe && a.copy_id == b.copy_id; +} + +namespace std +{ + template<> struct hash + { + inline size_t operator()(const heap_mvcc_copy_id_t &s) const + { + size_t seed = std::hash()(s.oid); + // Copy-pasted from spp::hash_combine() + seed ^= (s.copy_id + 0xc6a4a7935bd1e995 + (seed << 6) + (seed >> 2)); + return seed; + } + }; +}; + struct heap_object_mvcc_t { uint32_t readers = 0; @@ -149,7 +174,7 @@ class blockstore_heap_t uint32_t meta_alloc_count = 0; uint64_t meta_used_space = 0; multilist_alloc_t *buffer_alloc = NULL; - std::map object_mvcc; + std::unordered_map object_mvcc; std::unordered_map mvcc_data_refs; std::unordered_map mvcc_buffer_refs; std::map inode_space_stats; @@ -184,6 +209,7 @@ class blockstore_heap_t void copy_full_object(uint8_t *dst, heap_object_t *obj); bool mvcc_save_copy(heap_object_t *obj); bool mvcc_check_tracking(object_id oid); + void free_mvcc(std::unordered_map::iterator mvcc_it); void allocate_block(heap_block_info_t & inf); int add_object(object_id oid, heap_write_t *wr, uint32_t *modified_block); void mark_overwritten(uint64_t over_lsn, uint64_t inode, heap_write_t *wr, heap_write_t *end_wr, bool tracking_active);