/
smychkov
/
SStorage
Обзор
Документация
Войти
/
smychkov
/
SStorage
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
tests/test_lsm.cpp
229 строк
6 KB
Андрей Смычков
feat: LSM tree with levels, flush, compaction, recovery, block cache
25 апр 2026, 09:14
25 апр 2026, 09:14
4631f83
Код
Авторство
О чём код?
//============================================================================ // Тесты для LSMTree — end-to-end сценарии //============================================================================ #include "../src/lsm/lsm_tree.hpp" #include <cstdlib> #include <filesystem> #include <iostream> #include <string> #include <sys/stat.h> #include <unistd.h> using namespace sstorage; static int g_passed = 0; static int g_failed = 0; #define CHECK(cond) do { \ if (cond) { ++g_passed; } \ else { ++g_failed; std::cerr << "FAIL: " #cond " at line " << __LINE__ << "\n"; } \ } while (0) static std::string makeTempDir(const std::string& prefix) { std::string p = "/tmp/sstorage_lsm_" + prefix + "_" + std::to_string(::getpid()) + "_" + std::to_string(rand()); ::mkdir(p.c_str(), 0755); return p; } static void cleanupDir(const std::string& dir) { std::error_code ec; std::filesystem::remove_all(dir, ec); } int main() { // 1. Базовый put/get { auto dir = makeTempDir("basic"); LSMOptions opts; opts.memtableSizeBytes = 1024 * 1024; // 1MB, чтобы всё влезло LSMTree lsm(dir, opts); CHECK(lsm.open()); CHECK(lsm.put("key1", "value1")); CHECK(lsm.put("key2", "value2")); CHECK(lsm.put("key3", "value3")); auto v1 = lsm.get("key1"); CHECK(v1.has_value() && *v1 == "value1"); auto v2 = lsm.get("key2"); CHECK(v2.has_value() && *v2 == "value2"); CHECK(!lsm.get("nonexistent").has_value()); lsm.close(); cleanupDir(dir); } // 2. Upsert (перезапись существующего ключа) { auto dir = makeTempDir("upsert"); LSMTree lsm(dir, LSMOptions{}); lsm.open(); lsm.put("k", "v1"); lsm.put("k", "v2"); lsm.put("k", "v3"); auto v = lsm.get("k"); CHECK(v.has_value() && *v == "v3"); lsm.close(); cleanupDir(dir); } // 3. Remove — запись tombstone { auto dir = makeTempDir("remove"); LSMTree lsm(dir, LSMOptions{}); lsm.open(); lsm.put("k", "v"); CHECK(lsm.get("k").has_value()); CHECK(lsm.remove("k")); CHECK(!lsm.get("k").has_value()); // tombstone перекрывает value lsm.close(); cleanupDir(dir); } // 4. Flush на диск: после flush() значения всё ещё читаются { auto dir = makeTempDir("flush"); LSMTree lsm(dir, LSMOptions{}); lsm.open(); for (int i = 0; i < 100; ++i) { lsm.put("key" + std::to_string(i), "val" + std::to_string(i)); } CHECK(lsm.flush()); for (int i = 0; i < 100; ++i) { auto v = lsm.get("key" + std::to_string(i)); CHECK(v.has_value()); CHECK(*v == "val" + std::to_string(i)); } lsm.close(); cleanupDir(dir); } // 5. Persistence: close() + open() сохраняет данные { auto dir = makeTempDir("persist"); { LSMTree lsm(dir, LSMOptions{}); lsm.open(); for (int i = 0; i < 50; ++i) { lsm.put("k" + std::to_string(i), "v" + std::to_string(i)); } lsm.close(); } { LSMTree lsm(dir, LSMOptions{}); CHECK(lsm.open()); for (int i = 0; i < 50; ++i) { auto v = lsm.get("k" + std::to_string(i)); CHECK(v.has_value()); CHECK(*v == "v" + std::to_string(i)); } lsm.close(); } cleanupDir(dir); } // 6. Scan — диапазонный запрос { auto dir = makeTempDir("scan"); LSMTree lsm(dir, LSMOptions{}); lsm.open(); // Ключи с padding для правильной сортировки for (int i = 0; i < 100; ++i) { char k[16]; std::snprintf(k, sizeof(k), "key_%04d", i); lsm.put(k, "v_" + std::to_string(i)); } auto range = lsm.scan("key_0010", "key_0019", 0); CHECK(range.size() == 10); CHECK(range[0].first == "key_0010"); CHECK(range.back().first == "key_0019"); auto limited = lsm.scan("key_0000", "key_9999", 5); CHECK(limited.size() == 5); lsm.close(); cleanupDir(dir); } // 7. Много записей → автоматический flush → L0 файлы { auto dir = makeTempDir("autoflush"); LSMOptions opts; opts.memtableSizeBytes = 50 * 1024; // 50KB — быстро переполнится LSMTree lsm(dir, opts); lsm.open(); for (int i = 0; i < 1000; ++i) { char k[32]; std::snprintf(k, sizeof(k), "key_%06d", i); lsm.put(k, std::string(100, 'x')); } // Дождёмся фонового flush lsm.flush(); // Проверяем что всё на месте int found = 0; for (int i = 0; i < 1000; ++i) { char k[32]; std::snprintf(k, sizeof(k), "key_%06d", i); if (lsm.get(k).has_value()) ++found; } CHECK(found == 1000); // В stats должны быть файлы L0 auto s = lsm.stats(); CHECK(s.levelFileCounts[0] > 0); lsm.close(); cleanupDir(dir); } // 8. Tombstone persistence: put → remove → flush → close → open → get должен вернуть nullopt { auto dir = makeTempDir("tomb_persist"); { LSMTree lsm(dir, LSMOptions{}); lsm.open(); lsm.put("k", "v"); lsm.remove("k"); lsm.flush(); lsm.close(); } { LSMTree lsm(dir, LSMOptions{}); lsm.open(); CHECK(!lsm.get("k").has_value()); lsm.close(); } cleanupDir(dir); } // 9. Stats { auto dir = makeTempDir("stats"); LSMTree lsm(dir, LSMOptions{}); lsm.open(); lsm.put("k1", "v1"); lsm.put("k2", "v2"); auto s = lsm.stats(); CHECK(s.memtableSize > 0); lsm.close(); cleanupDir(dir); } std::cout << "test_lsm: passed=" << g_passed << " failed=" << g_failed << "\n"; return g_failed == 0 ? 0 : 1; }