Advertisement
scottish_esquire

Redka Compact Init ver 3.0

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