/
smychkov
/
SStorage
Обзор
Документация
Войти
/
smychkov
/
SStorage
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
tests/test_memtable.cpp
176 строк
5 KB
Андрей Смычков
feat: MemTable (std::map, freeze, drainSorted)
25 апр 2026, 09:04
25 апр 2026, 09:04
39a036a
Код
Авторство
О чём код?
//============================================================================ // Тесты для MemTable //============================================================================ #include "../src/memtable/memtable.hpp" #include <iostream> #include <thread> #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) int main() { // 1. Базовая вставка и чтение { MemTable mt; CHECK(mt.empty()); mt.put(Record("hello", "world", 1)); CHECK(!mt.empty()); CHECK(mt.recordCount() == 1); auto r = mt.get("hello"); CHECK(r.has_value()); CHECK(r->value() == "world"); CHECK(r->seqNo() == 1); CHECK(!mt.get("missing").has_value()); } // 2. Перезапись: более новый seqNo побеждает { MemTable mt; mt.put(Record("k", "v1", 1)); mt.put(Record("k", "v2", 2)); auto r = mt.get("k"); CHECK(r.has_value()); CHECK(r->value() == "v2"); CHECK(r->seqNo() == 2); CHECK(mt.recordCount() == 1); // один ключ } // 3. Более старый seqNo игнорируется { MemTable mt; mt.put(Record("k", "v2", 2)); mt.put(Record("k", "v1", 1)); // старше — игнор auto r = mt.get("k"); CHECK(r->value() == "v2"); CHECK(r->seqNo() == 2); } // 4. Tombstone — возвращается, т.к. tombstone и есть "удалённая версия" { MemTable mt; mt.put(Record("k", "v", 1)); mt.put(Record::makeTombstone("k", 2)); auto r = mt.get("k"); CHECK(r.has_value()); CHECK(r->isTombstone()); } // 5. Scan — упорядоченность и диапазон { MemTable mt; mt.put(Record("b", "vb", 1)); mt.put(Record("a", "va", 1)); mt.put(Record("d", "vd", 1)); mt.put(Record("c", "vc", 1)); mt.put(Record("e", "ve", 1)); auto all = mt.scan("a", "e", 0); CHECK(all.size() == 5); CHECK(all[0].key() == "a"); CHECK(all[1].key() == "b"); CHECK(all[2].key() == "c"); CHECK(all[3].key() == "d"); CHECK(all[4].key() == "e"); auto mid = mt.scan("b", "d", 0); CHECK(mid.size() == 3); CHECK(mid[0].key() == "b"); CHECK(mid[2].key() == "d"); auto limited = mt.scan("a", "e", 2); CHECK(limited.size() == 2); CHECK(limited[0].key() == "a"); CHECK(limited[1].key() == "b"); auto empty = mt.scan("x", "z", 0); CHECK(empty.empty()); } // 6. approximateSize растёт с записями { MemTable mt; size_t initial = mt.approximateSize(); CHECK(initial == 0); mt.put(Record("key1", "value1", 1)); CHECK(mt.approximateSize() > initial); size_t after1 = mt.approximateSize(); mt.put(Record("key2", "value2", 2)); CHECK(mt.approximateSize() > after1); } // 7. Freeze: после заморозки put игнорируется { MemTable mt; mt.put(Record("k", "v", 1)); CHECK(!mt.isFrozen()); mt.freeze(); CHECK(mt.isFrozen()); mt.put(Record("k2", "v2", 2)); CHECK(!mt.get("k2").has_value()); // не записалось } // 8. drainSorted — отсортированный порядок { MemTable mt; mt.put(Record("zebra", "v", 1)); mt.put(Record("alpha", "v", 1)); mt.put(Record("mango", "v", 1)); auto drained = mt.drainSorted(); CHECK(drained.size() == 3); CHECK(drained[0].key() == "alpha"); CHECK(drained[1].key() == "mango"); CHECK(drained[2].key() == "zebra"); } // 9. Concurrency: параллельные put из разных потоков { MemTable mt; constexpr int kThreads = 8; constexpr int kPerThread = 1000; std::vector<std::thread> threads; for (int t = 0; t < kThreads; ++t) { threads.emplace_back([&, t]() { for (int i = 0; i < kPerThread; ++i) { std::string key = "t" + std::to_string(t) + "_k" + std::to_string(i); mt.put(Record(key, "v", t * 10000 + i)); } }); } for (auto& th : threads) th.join(); CHECK(mt.recordCount() == kThreads * kPerThread); } // 10. Concurrency: параллельные readers не блокируют друг друга { MemTable mt; for (int i = 0; i < 1000; ++i) { mt.put(Record("k" + std::to_string(i), "v", i)); } std::atomic<int> found{0}; std::vector<std::thread> threads; for (int t = 0; t < 8; ++t) { threads.emplace_back([&]() { for (int i = 0; i < 1000; ++i) { auto r = mt.get("k" + std::to_string(i)); if (r) ++found; } }); } for (auto& th : threads) th.join(); CHECK(found.load() == 8 * 1000); } std::cout << "test_memtable: passed=" << g_passed << " failed=" << g_failed << "\n"; return g_failed == 0 ? 0 : 1; }