unordered_map mvcc

This commit is contained in:
Vitaliy Filippov
2025-11-23 19:08:24 +03:00
parent d4d3d34a84
commit 2a550d4d13
2 changed files with 111 additions and 56 deletions
+84 -55
View File
@@ -857,33 +857,43 @@ heap_object_t *blockstore_heap_t::lock_and_read_entry(object_id oid, uint64_t &
{ {
return NULL; return NULL;
} }
auto mvcc_it = object_mvcc.lower_bound({ .oid = { .inode = oid.inode, .stripe = oid.stripe+1 }, .lsn = 0 }); heap_mvcc_copy_id_t mvcc_id = { .oid = { .inode = oid.inode, .stripe = oid.stripe }, .copy_id = 1 };
copy_id = 1; auto mvcc_it = object_mvcc.find(mvcc_id);
if (mvcc_it != object_mvcc.begin()) if (mvcc_it != object_mvcc.end())
{ {
mvcc_it--; while (true)
if (mvcc_it->first.oid == oid)
{ {
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 mvcc_id.copy_id--;
copy_id = mvcc_it->first.lsn+1; break;
}
else
{
copy_id = mvcc_it->first.lsn;
mvcc_it->second.readers++;
return obj;
} }
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; return obj;
} }
heap_object_t *blockstore_heap_t::read_locked_entry(object_id oid, uint64_t copy_id) 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()) if (mvcc_it == object_mvcc.end())
{ {
return NULL; 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); return read_entry(oid, NULL);
} }
void blockstore_heap_t::free_mvcc(std::unordered_map<heap_mvcc_copy_id_t, heap_object_mvcc_t>::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) 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()) if (mvcc_it == object_mvcc.end())
{ {
return false; return false;
@@ -905,37 +928,37 @@ bool blockstore_heap_t::unlock_entry(object_id oid, uint64_t copy_id)
mvcc_it->second.readers--; mvcc_it->second.readers--;
if (!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 // Next entry isn't freed yet
heap_object_t *obj = (heap_object_t*)mvcc_it->second.entry_copy; return true;
free_object_space(obj->inode, obj->get_writes(), NULL, BS_HEAP_FREE_MVCC); }
bool is_last_mvcc = true; mvcc_id.copy_id--;
if (mvcc_it != object_mvcc.end()) // Free this entry
{ free_mvcc(mvcc_it);
// object_mvcc may contain multiple copied entries, but always in a whole sequence // Free all previous entries with 0 refcount
auto next_it = std::next(mvcc_it); while (mvcc_id.copy_id > 1)
if (next_it->first.oid == oid && next_it->second.entry_copy) {
is_last_mvcc = false; mvcc_id.copy_id--;
} mvcc_it = object_mvcc.find(mvcc_id);
if (is_last_mvcc && mvcc_it != object_mvcc.begin()) assert(mvcc_it != object_mvcc.end());
{ if (mvcc_it->second.readers)
auto prev_it = std::prev(mvcc_it); {
if (prev_it->first.oid == oid && prev_it->second.entry_copy) mvcc_id.copy_id++;
is_last_mvcc = false; break;
} }
if (is_last_mvcc) free_mvcc(mvcc_it);
{ }
// Free data references from the newest object version when the last MVCC is freed if (mvcc_id.copy_id == 1)
heap_object_t *new_obj = read_entry(oid, NULL); {
if (new_obj) // Free data references from the newest object version when the last MVCC is freed
{ heap_object_t *new_obj = read_entry(oid, NULL);
free_object_space(new_obj->inode, new_obj->get_writes(), NULL, BS_HEAP_FREE_MAIN); if (new_obj)
} {
} free_object_space(new_obj->inode, new_obj->get_writes(), NULL, BS_HEAP_FREE_MAIN);
free(mvcc_it->second.entry_copy); }
} }
object_mvcc.erase(mvcc_it);
} }
return true; 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) 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()) if (mvcc_it == object_mvcc.end())
{ {
// no copies // no copies
return false; 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) 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) bool blockstore_heap_t::mvcc_save_copy(heap_object_t *obj)
{ {
auto oid = (object_id){ .inode = obj->inode, .stripe = obj->stripe }; 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 }); auto mvcc_id = (heap_mvcc_copy_id_t){ .oid = oid, .copy_id = 1 };
if (mvcc_it == object_mvcc.begin()) auto mvcc_it = object_mvcc.find(mvcc_id);
if (mvcc_it == object_mvcc.end())
{ {
// no copies // no copies
return false; return false;
} }
mvcc_it--; while (true)
if (mvcc_it->first.oid != oid)
{ {
// no copies :-) mvcc_id.copy_id++;
return false; 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) 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; bool for_obj = false;
// save_copy is performed when the object is modified, so object_mvcc may only // save_copy is performed when the object is modified, so object_mvcc may only
// contain 1 version with entry_copy == NULL // 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 // Init refcounts for the copy and for the object itself, when it's the first MVCC entry
add_ref = 2; add_ref = 2;
+27 -1
View File
@@ -97,6 +97,31 @@ struct tmp_compact_item_t
bool compact; 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<heap_mvcc_copy_id_t>
{
inline size_t operator()(const heap_mvcc_copy_id_t &s) const
{
size_t seed = std::hash<object_id>()(s.oid);
// Copy-pasted from spp::hash_combine()
seed ^= (s.copy_id + 0xc6a4a7935bd1e995 + (seed << 6) + (seed >> 2));
return seed;
}
};
};
struct heap_object_mvcc_t struct heap_object_mvcc_t
{ {
uint32_t readers = 0; uint32_t readers = 0;
@@ -149,7 +174,7 @@ class blockstore_heap_t
uint32_t meta_alloc_count = 0; uint32_t meta_alloc_count = 0;
uint64_t meta_used_space = 0; uint64_t meta_used_space = 0;
multilist_alloc_t *buffer_alloc = NULL; multilist_alloc_t *buffer_alloc = NULL;
std::map<heap_object_lsn_t, heap_object_mvcc_t> object_mvcc; std::unordered_map<heap_mvcc_copy_id_t, heap_object_mvcc_t> object_mvcc;
std::unordered_map<uint64_t, uint32_t> mvcc_data_refs; std::unordered_map<uint64_t, uint32_t> mvcc_data_refs;
std::unordered_map<uint64_t, uint32_t> mvcc_buffer_refs; std::unordered_map<uint64_t, uint32_t> mvcc_buffer_refs;
std::map<uint64_t, uint64_t> inode_space_stats; std::map<uint64_t, uint64_t> inode_space_stats;
@@ -184,6 +209,7 @@ class blockstore_heap_t
void copy_full_object(uint8_t *dst, heap_object_t *obj); void copy_full_object(uint8_t *dst, heap_object_t *obj);
bool mvcc_save_copy(heap_object_t *obj); bool mvcc_save_copy(heap_object_t *obj);
bool mvcc_check_tracking(object_id oid); bool mvcc_check_tracking(object_id oid);
void free_mvcc(std::unordered_map<heap_mvcc_copy_id_t, heap_object_mvcc_t>::iterator mvcc_it);
void allocate_block(heap_block_info_t & inf); void allocate_block(heap_block_info_t & inf);
int add_object(object_id oid, heap_write_t *wr, uint32_t *modified_block); 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); void mark_overwritten(uint64_t over_lsn, uint64_t inode, heap_write_t *wr, heap_write_t *end_wr, bool tracking_active);