/
smychkov
/
SStorage
Обзор
Документация
Войти
/
smychkov
/
SStorage
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
tests/test_sstable.cpp
217 строк
7 KB
Андрей Смычков
feat: SSTable writer + reader with atomic writes
25 апр 2026, 09:09
25 апр 2026, 09:09
79efb83
Код
Авторство
О чём код?
//============================================================================ // Тесты для SSTable writer + reader (roundtrip, find, scan, большие файлы) //============================================================================ #include "../src/sstable/sstable_writer.hpp" #include "../src/sstable/sstable_reader.hpp" #include <cstdlib> #include <filesystem> #include <iostream> #include <string> #include <sys/stat.h> #include <unistd.h> #include <vector> 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 makeTempPath(const std::string& prefix) { return "/tmp/sstorage_" + prefix + "_" + std::to_string(::getpid()) + "_" + std::to_string(rand()) + ".sst"; } int main() { // 1. Простой roundtrip { auto path = makeTempPath("simple"); { SSTableWriter w(path, 4096, util::CompressionType::kNone); CHECK(w.open()); CHECK(w.add(Record("key1", "value1", 1))); CHECK(w.add(Record("key2", "value2", 2))); CHECK(w.add(Record("key3", "value3", 3))); CHECK(w.finish()); } SSTableReader r(path); CHECK(r.open()); CHECK(r.numRecords() == 3); CHECK(r.minKey() == "key1"); CHECK(r.maxKey() == "key3"); auto r1 = r.find("key1"); CHECK(r1.has_value()); CHECK(r1->value() == "value1"); auto r2 = r.find("key2"); CHECK(r2.has_value() && r2->value() == "value2"); CHECK(!r.find("key0").has_value()); CHECK(!r.find("key4").has_value()); CHECK(!r.find("nonexistent").has_value()); std::filesystem::remove(path); } // 2. Tombstone { auto path = makeTempPath("tomb"); { SSTableWriter w(path, 4096); w.open(); w.add(Record("a", "va", 1)); w.add(Record::makeTombstone("b", 2)); w.add(Record("c", "vc", 3)); w.finish(); } SSTableReader r(path); r.open(); auto t = r.find("b"); CHECK(t.has_value()); CHECK(t->isTombstone()); std::filesystem::remove(path); } // 3. Большой SSTable, много блоков { auto path = makeTempPath("big"); const int kRecords = 10000; { SSTableWriter w(path, 4096, util::CompressionType::kSnappy); w.open(); for (int i = 0; i < kRecords; ++i) { // Ключи с zero-pad для правильной лексикографической сортировки char key[16]; std::snprintf(key, sizeof(key), "key_%08d", i); w.add(Record(key, "value_" + std::to_string(i), i)); } CHECK(w.finish()); } SSTableReader r(path); CHECK(r.open()); CHECK(r.numRecords() == kRecords); // Точечные поиски for (int i : {0, 1, 100, 5000, 9999}) { char key[16]; std::snprintf(key, sizeof(key), "key_%08d", i); auto rec = r.find(key); CHECK(rec.has_value()); CHECK(rec->value() == "value_" + std::to_string(i)); } // Scan диапазона auto range = r.scan("key_00000100", "key_00000200", 0); CHECK(range.size() == 101); // 100..200 inclusive CHECK(range.front().key() == "key_00000100"); CHECK(range.back().key() == "key_00000200"); // Scan с лимитом auto limited = r.scan("key_00000000", "key_99999999", 10); CHECK(limited.size() == 10); // Scan за пределами auto empty = r.scan("zzz_start", "zzz_end", 0); CHECK(empty.empty()); std::filesystem::remove(path); } // 4. Пустой SSTable { auto path = makeTempPath("empty"); { SSTableWriter w(path, 4096); w.open(); CHECK(w.finish()); } SSTableReader r(path); CHECK(r.open()); CHECK(r.numRecords() == 0); CHECK(!r.find("anything").has_value()); CHECK(r.scan("a", "z", 0).empty()); std::filesystem::remove(path); } // 5. readAll — для compaction { auto path = makeTempPath("readall"); { SSTableWriter w(path, 256); // маленькие блоки для проверки w.open(); for (int i = 0; i < 50; ++i) { char k[16]; std::snprintf(k, sizeof(k), "k_%04d", i); w.add(Record(k, std::string(50, 'x'), i)); } w.finish(); } SSTableReader r(path); r.open(); auto all = r.readAll(); CHECK(all.size() == 50); for (int i = 0; i < 50; ++i) { char k[16]; std::snprintf(k, sizeof(k), "k_%04d", i); CHECK(all[i].key() == k); } std::filesystem::remove(path); } // 6. overlaps / mayContain { auto path = makeTempPath("overlap"); { SSTableWriter w(path, 4096); w.open(); for (char c : {'c', 'd', 'e', 'f'}) { w.add(Record(std::string(1, c), "v", 1)); } w.finish(); } SSTableReader r(path); r.open(); CHECK(r.overlaps("a", "c")); // граница CHECK(r.overlaps("c", "f")); // полностью внутри CHECK(r.overlaps("a", "z")); // вокруг CHECK(!r.overlaps("a", "b")); // левее CHECK(!r.overlaps("g", "z")); // правее CHECK(r.mayContain("c")); CHECK(!r.mayContain("z")); // вне диапазона std::filesystem::remove(path); } // 7. Atomic write: при crash между open и finish — tmp-файл не видим { auto path = makeTempPath("atomic"); { SSTableWriter w(path, 4096); w.open(); w.add(Record("k", "v", 1)); // НЕ вызываем finish() } // Файл path должен отсутствовать struct stat st; CHECK(::stat(path.c_str(), &st) != 0); // tmp-файл тоже удалён деструктором std::string tmp = path + ".tmp"; CHECK(::stat(tmp.c_str(), &st) != 0); } std::cout << "test_sstable: passed=" << g_passed << " failed=" << g_failed << "\n"; return g_failed == 0 ? 0 : 1; }