Advertisement
scottish_esquire

Redka Compact Init ver 5.0

Mar 27th, 2025
219
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 12.88 KB | None | 0 0
  1. #include <iostream>
  2. #include <fstream>
  3. #include <string>
  4. #include <vector>
  5. #include <map>
  6. #include <filesystem>
  7. #include <chrono>
  8. #include <thread>
  9. #include <algorithm>
  10. #include <cmath>
  11. #include <sstream>
  12. #include <regex>
  13.  
  14. namespace fs = std::filesystem;
  15.  
  16. // Конфигурация
  17. const size_t WAL_SIZE_THRESHOLD = 1024; // 1KB
  18. const std::string WAL_FILENAME = "wal.log";
  19. const size_t LEVEL_BASE_SIZE = 10;
  20. const std::string DB_DIR = "lsm_db";
  21.  
  22. #pragma pack(push, 1)
  23. struct SSTHeader {
  24.     uint32_t version = 2; // Версия 2 для поддержки версионирования полей
  25.     uint32_t entry_count;
  26.     uint64_t index_offset;
  27. };
  28.  
  29. struct SSTIndexEntry {
  30.     uint32_t key_length;
  31.     uint64_t data_offset;
  32.     uint32_t data_length;
  33. };
  34. #pragma pack(pop)
  35.  
  36. struct FieldValue {
  37.     uint32_t version;
  38.     std::string value;
  39. };
  40.  
  41. struct WalEntry {
  42.     std::string key;
  43.     std::map<std::string, FieldValue> fields;
  44. };
  45.  
  46. struct SSTEntry {
  47.     std::string key;
  48.     std::map<std::string, FieldValue> fields;
  49.  
  50.     bool operator<(const SSTEntry& other) const {
  51.         return key < other.key;
  52.     }
  53. };
  54.  
  55. class LSMTree {
  56. private:
  57.     std::vector<std::vector<std::string>> levels;
  58.  
  59.     void ensureDbDir() {
  60.         if (!fs::exists(DB_DIR)) {
  61.             fs::create_directory(DB_DIR);
  62.             for (int i = 0; i < 10; ++i) {
  63.                 fs::create_directory(DB_DIR + "/L" + std::to_string(i));
  64.             }
  65.         }
  66.     }
  67.  
  68.     void loadLevels() {
  69.         levels.clear();
  70.         for (int i = 0; ; ++i) {
  71.             std::string level_dir = DB_DIR + "/L" + std::to_string(i);
  72.             if (!fs::exists(level_dir)) break;
  73.  
  74.             std::vector<std::string> files;
  75.             for (const auto& entry : fs::directory_iterator(level_dir)) {
  76.                 if (entry.path().extension() == ".sst") {
  77.                     files.push_back(entry.path().string());
  78.                 }
  79.             }
  80.             std::sort(files.begin(), files.end());
  81.             std::reverse(files.begin(), files.end());
  82.             levels.push_back(files);
  83.         }
  84.     }
  85.  
  86.     void mergeEntries(SSTEntry& target, const SSTEntry& source) {
  87.         for (const auto& [field, src_val] : source.fields) {
  88.             auto it = target.fields.find(field);
  89.             if (it == target.fields.end() || src_val.version > it->second.version) {
  90.                 target.fields[field] = src_val;
  91.             }
  92.         }
  93.     }
  94.  
  95.     void compactLevel(int level) {
  96.         if (level >= levels.size()) return;
  97.        
  98.         std::map<std::string, SSTEntry> merged_entries;
  99.        
  100.         // Собираем и мержим все записи с текущего уровня
  101.         for (const auto& sst_path : levels[level]) {
  102.             auto entries = readSST(sst_path);
  103.             for (const auto& entry : entries) {
  104.                 if (merged_entries.find(entry.key) == merged_entries.end()) {
  105.                     merged_entries[entry.key] = entry;
  106.                 } else {
  107.                     mergeEntries(merged_entries[entry.key], entry);
  108.                 }
  109.             }
  110.         }
  111.    
  112.         if (merged_entries.size() >= LEVEL_BASE_SIZE * std::pow(10, level)) {
  113.             // Преобразуем map в vector для записи
  114.             std::vector<SSTEntry> entries_to_write;
  115.             for (auto& [key, entry] : merged_entries) {
  116.                 entries_to_write.push_back(std::move(entry));
  117.             }
  118.             std::sort(entries_to_write.begin(), entries_to_write.end());
  119.    
  120.             // Записываем на следующий уровень
  121.             std::string new_sst = DB_DIR + "/L" + std::to_string(level + 1) + "/" +
  122.                                  std::to_string(std::chrono::system_clock::now().time_since_epoch().count()) + ".sst";
  123.             writeSST(new_sst, entries_to_write);
  124.    
  125.             // Удаляем старые файлы
  126.             for (const auto& sst_path : levels[level]) {
  127.                 fs::remove(sst_path);
  128.             }
  129.    
  130.             // Обновляем индексы и рекурсивно уплотняем
  131.             loadLevels();
  132.             compactLevel(level + 1);
  133.         }
  134.     }
  135.  
  136.     std::vector<SSTEntry> readSST(const std::string& path) {
  137.         std::ifstream file(path, std::ios::binary);
  138.         if (!file) return {};
  139.  
  140.         SSTHeader header;
  141.         file.read(reinterpret_cast<char*>(&header), sizeof(header));
  142.  
  143.         std::vector<SSTIndexEntry> index(header.entry_count);
  144.         file.seekg(header.index_offset);
  145.         file.read(reinterpret_cast<char*>(index.data()), header.entry_count * sizeof(SSTIndexEntry));
  146.  
  147.         std::vector<SSTEntry> entries;
  148.         for (const auto& idx : index) {
  149.             file.seekg(idx.data_offset);
  150.            
  151.             uint32_t total_len;
  152.             file.read(reinterpret_cast<char*>(&total_len), sizeof(total_len));
  153.  
  154.             std::string key(idx.key_length, '\0');
  155.             file.read(&key[0], idx.key_length);
  156.  
  157.             std::string fields_data(total_len - idx.key_length, '\0');
  158.             file.read(&fields_data[0], fields_data.size());
  159.  
  160.             SSTEntry entry;
  161.             entry.key = key;
  162.             entry.fields = parseFields(fields_data);
  163.             entries.push_back(entry);
  164.         }
  165.  
  166.         return entries;
  167.     }
  168.  
  169.     std::map<std::string, FieldValue> parseFields(const std::string& data) {
  170.         std::map<std::string, FieldValue> fields;
  171.         std::regex field_re(R"((\w+)(@(\d+))?:([^ ]+))");
  172.         std::smatch match;
  173.         std::string::const_iterator search_start(data.cbegin());
  174.  
  175.         while (std::regex_search(search_start, data.cend(), match, field_re)) {
  176.             std::string field = match[1].str();
  177.             uint32_t version = match[3].matched ? std::stoul(match[3].str()) : 1;
  178.             std::string value = match[4].str();
  179.  
  180.             fields[field] = {version, value};
  181.             search_start = match[0].second;
  182.         }
  183.  
  184.         return fields;
  185.     }
  186.  
  187.     std::string serializeFields(const std::map<std::string, FieldValue>& fields) {
  188.         std::stringstream ss;
  189.         ss << "{";
  190.         bool first = true;
  191.         for (const auto& [field, fv] : fields) {
  192.             if (!first) ss << " ";
  193.             first = false;
  194.             if (fv.version > 1) {
  195.                 ss << field << "@" << fv.version << ":" << fv.value;
  196.             } else {
  197.                 ss << field << ":" << fv.value;
  198.             }
  199.         }
  200.         ss << "}";
  201.         return ss.str();
  202.     }
  203.  
  204.     void writeSST(const std::string& path, const std::vector<SSTEntry>& entries) {
  205.         std::ofstream file(path, std::ios::binary);
  206.         SSTHeader header;
  207.         header.entry_count = entries.size();
  208.         file.write(reinterpret_cast<char*>(&header), sizeof(header));
  209.  
  210.         std::vector<SSTIndexEntry> index;
  211.         for (const auto& entry : entries) {
  212.             SSTIndexEntry idx;
  213.             idx.key_length = entry.key.size();
  214.             idx.data_offset = file.tellp();
  215.  
  216.             std::string fields_data = serializeFields(entry.fields);
  217.             uint32_t total_len = entry.key.size() + fields_data.size();
  218.            
  219.             file.write(reinterpret_cast<char*>(&total_len), sizeof(total_len));
  220.             file.write(entry.key.data(), entry.key.size());
  221.             file.write(fields_data.data(), fields_data.size());
  222.  
  223.             idx.data_length = total_len;
  224.             index.push_back(idx);
  225.         }
  226.  
  227.         header.index_offset = file.tellp();
  228.         for (const auto& idx : index) {
  229.             file.write(reinterpret_cast<const char*>(&idx), sizeof(idx));
  230.         }
  231.  
  232.         file.seekp(0);
  233.         file.write(reinterpret_cast<char*>(&header), sizeof(header));
  234.     }
  235.  
  236.     std::vector<WalEntry> readWalFile(const std::string& filename) {
  237.         std::vector<WalEntry> entries;
  238.         std::ifstream wal(filename);
  239.         if (!wal) return entries;
  240.    
  241.         std::string line;
  242.         while (std::getline(wal, line)) {
  243.             if (line.empty()) continue;
  244.    
  245.             size_t key_start = line.find('{');
  246.             size_t key_end = line.find(' ', key_start);
  247.             size_t data_start = line.find('{', key_end);
  248.             size_t data_end = line.rfind('}');
  249.    
  250.             if (key_start == std::string::npos || key_end == std::string::npos ||
  251.                 data_start == std::string::npos || data_end == std::string::npos) {
  252.                 std::cerr << "Invalid WAL entry: " << line << std::endl;
  253.                 continue;
  254.             }
  255.    
  256.             std::string key = line.substr(key_start + 2, key_end - (key_start + 2));
  257.             std::string fields_data = line.substr(data_start, data_end - data_start + 1);
  258.    
  259.             WalEntry entry;
  260.             entry.key = key;
  261.             entry.fields = parseFields(fields_data);
  262.             entries.push_back(entry);
  263.         }
  264.    
  265.         return entries;
  266.     }
  267.  
  268. public:
  269.     LSMTree() {
  270.         ensureDbDir();
  271.         loadLevels();
  272.     }
  273.  
  274.     void put(const std::string& key, const std::string& value) {
  275.         uint64_t timestamp = std::chrono::system_clock::now().time_since_epoch().count();
  276.         std::ofstream wal(WAL_FILENAME, std::ios::app);
  277.         wal << "{@" << key << " " << value << "}\n";
  278.         wal.close();
  279.    
  280.         if (fs::file_size(WAL_FILENAME) >= WAL_SIZE_THRESHOLD) {
  281.             flushWalToL0();
  282.         }
  283.     }
  284.  
  285.     void flushWalToL0() {
  286.         auto wal_entries = readWalFile(WAL_FILENAME);
  287.         std::map<std::string, SSTEntry> latest_entries;
  288.        
  289.         for (const auto& wal_entry : wal_entries) {
  290.             if (latest_entries.find(wal_entry.key) == latest_entries.end()) {
  291.                 SSTEntry entry;
  292.                 entry.key = wal_entry.key;
  293.                 latest_entries[wal_entry.key] = entry;
  294.             }
  295.             for (const auto& [field, fv] : wal_entry.fields) {
  296.                 latest_entries[wal_entry.key].fields[field] = fv;
  297.             }
  298.         }
  299.    
  300.         std::vector<SSTEntry> entries;
  301.         for (auto& [key, entry] : latest_entries) {
  302.             entries.push_back(std::move(entry));
  303.         }
  304.    
  305.         std::string sst_path = DB_DIR + "/L0/" +
  306.                               std::to_string(std::chrono::system_clock::now().time_since_epoch().count()) + ".sst";
  307.         writeSST(sst_path, entries);
  308.    
  309.         std::ofstream wal(WAL_FILENAME, std::ios::trunc);
  310.         loadLevels();
  311.         compactLevel(0);
  312.     }
  313.  
  314.     std::string get(const std::string& key) {
  315.         std::map<std::string, FieldValue> merged_fields;
  316.    
  317.         // Проверяем WAL
  318.         auto wal_entries = readWalFile(WAL_FILENAME);
  319.         for (const auto& entry : wal_entries) {
  320.             if (entry.key == key) {
  321.                 for (const auto& [field, fv] : entry.fields) {
  322.                     if (fv.version > merged_fields[field].version) {
  323.                         merged_fields[field] = fv;
  324.                     }
  325.                 }
  326.             }
  327.         }
  328.    
  329.         // Ищем в SST-файлах
  330.         for (const auto& level : levels) {
  331.             for (const auto& sst_path : level) {
  332.                 auto entries = readSST(sst_path);
  333.                 auto it = std::lower_bound(
  334.                     entries.begin(), entries.end(), key,
  335.                     [](const SSTEntry& e, const std::string& k) { return e.key < k; });
  336.    
  337.                 if (it != entries.end() && it->key == key) {
  338.                     for (const auto& [field, fv] : it->fields) {
  339.                         if (fv.version > merged_fields[field].version) {
  340.                             merged_fields[field] = fv;
  341.                         }
  342.                     }
  343.                 }
  344.             }
  345.         }
  346.        
  347.         return merged_fields.empty() ? "" : serializeFields(merged_fields);
  348.     }
  349. };
  350.  
  351. int main() {
  352.     LSMTree db;
  353.    
  354.     // Тестирование версионирования
  355.     // db.put("b0b-1", "{name:\"Alena\" age:25}");
  356.     // std::this_thread::sleep_for(std::chrono::milliseconds(10));
  357.     // db.put("b0b-1", "{name:\"Dima\" age:26 address:\"Moscow\"}");
  358.     // std::this_thread::sleep_for(std::chrono::milliseconds(10));
  359.     // db.put("b0b-1", "{address@2:\"SPb\"}");
  360.  
  361.     // for (size_t i = 0; i < 1; i++) {
  362.     //     db.put("b0b-5", "{name:\"Dima\"}");
  363.     //     db.put("b0b-6", "{name:\"Dima\"}");
  364.     //     db.put("b0b-7", "{name:\"Dima\"}");
  365.     //     db.put("b0b-8", "{name:\"Dima\"}");
  366.     //     db.put("b0b-9", "{name:\"Dima\"}");
  367.     //     db.put("b0b-10", "{name:\"Dima\"}");
  368.     //     db.put("b0b-11", "{name:\"Dima\"}");
  369.     //     db.put("b0b-12", "{name:\"Dima\"}");
  370.     //     db.put("b0b-13", "{name:\"Dima\"}");
  371.     // }
  372.    
  373.     // db.flushWalToL0();
  374.    
  375.     std::cout << "b0b-1: " << db.get("b0b-1") << std::endl;
  376.     // Вывод: {address@2:SPb age:26 name:Alena}
  377.     std::cout << "b0b-4: " << db.get("b0b-4") << std::endl;
  378.    
  379.     return 0;
  380. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement