// // Created by Kirill Zhukov on 20.04.2025. // #ifndef MEMTABLE_H #define MEMTABLE_H #include #include #include #include "utils/io/Wal.h" #include "utils/io/VersionManager.h" #include "utils/config/GlobalConfig.h" namespace usub::shared_storage { using namespace usub::utils; template class MemTableManager { public: MemTableManager(const std::string& wal_file, size_t max_size, size_t estimated_element_size, utils::VersionManager& vm); ~MemTableManager(); void put(const typename SkipList::key_type& key, const typename SkipList::value_type& value); void remove(const typename SkipList::key_type& key); std::optional get(const typename SkipList::key_type& key); void flush(); void flush_batch(); private: std::atomic active_memtable; WAL wal; size_t max_memtable_size; std::mutex batch_mutex; std::vector> write_batch; std::atomic flushing{false}; VersionManager& version_manager; size_t estimated_element_size; private: size_t estimate_memtable_size() const; size_t estimate_batch_size() const; }; template MemTableManager::MemTableManager(const std::string& wal_file, size_t max_size, size_t estimated_element_size, VersionManager& vm) : wal(wal_file), max_memtable_size(max_size), version_manager(vm) { this->active_memtable.store(new SkipList(this->version_manager)); } template MemTableManager::~MemTableManager() { delete this->active_memtable.load(); } template void MemTableManager::put(const typename SkipList::key_type& key, const typename SkipList::value_type& value) { { std::lock_guard lock(this->batch_mutex); this->write_batch.emplace_back(key, value); } if (estimate_batch_size() >= 64) { flush_batch(); } } template void MemTableManager::remove(const typename SkipList::key_type& key) { this->wal.write_delete(key); this->active_memtable.load()->erase(key); if (estimate_memtable_size() > this->max_memtable_size) { flush(); } } template std::optional MemTableManager::get(const typename SkipList::key_type& key) { return this->active_memtable.load()->find(key); } template void MemTableManager::flush() { if (this->flushing.exchange(true)) return; auto old_memtable = this->active_memtable.exchange(new SkipList(this->version_manager)); std::string filename = "sstable_" + std::to_string(std::time(nullptr)) + ".dat"; write_sstable_with_index(*old_memtable, filename); delete old_memtable; this->wal.close(); this->flushing.store(false); } template void MemTableManager::flush_batch() { std::vector> local_batch; { std::lock_guard lock(this->batch_mutex); local_batch.swap(this->write_batch); } for (const auto& [key, value] : local_batch) { this->wal.write_put(key, value); this->active_memtable.load()->insert(key, value); } if (estimate_memtable_size() > this->max_memtable_size) { flush(); } } template size_t MemTableManager::estimate_memtable_size() const { // For simplicity: count the number of elements * average size return this->active_memtable.load()->unsafe_size() * 128; // The error is acceptable } template size_t MemTableManager::estimate_batch_size() const { return this->write_batch.size(); } } // shared_storage // usub #endif //MEMTABLE_H