Loading src/backends/cluster.cpp +61 −13 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ #include <algorithm> #include <thread> #include <chrono> #include <future> #include <map> #include <tuple> #include <shared_mutex> Loading Loading @@ -294,12 +295,58 @@ namespace authdb { auto &client = g_Cluster->getReadClient(); if (!client) return; // Single retrieve: manifest contains header + magic + entity keys + full record data // Single retrieve with timeout: manifest contains header + magic + entity keys + full record data std::vector<uint8_t> manifest; // Check if a previous timed-out fetch completed in the background if (cache->pending_retrieve.valid()) { auto st = cache->pending_retrieve.wait_for(std::chrono::milliseconds(0)); if (st == std::future_status::ready) { try { manifest = client->retrieve(dgid); manifest = cache->pending_retrieve.get(); cache->last_fetch = now; cache->recovery_epoch = cur_epoch; } catch (const std::exception &e) { std::cerr << "ClusterBackend::fetchFromCluster: background retrieve failed: " << e.what() << std::endl; _ClusterDataExists.store(true); return; } catch (...) { _ClusterDataExists.store(true); return; } } else { // Previous fetch still running — use cached data, don't start another if (!cache->buffer.empty()) { _Buffer = cache->buffer; _ClusterDataExists.store(_Buffer.size() > sizeof(AuthHeader)); } return; } } else { // No pending fetch — start a new async retrieve with timeout try { auto *raw_client = client.get(); cache->pending_retrieve = std::async(std::launch::async, [raw_client, dgid]() { return raw_client->retrieve(dgid); }); static constexpr auto RETRIEVE_TIMEOUT = std::chrono::seconds(5); if (cache->pending_retrieve.wait_for(RETRIEVE_TIMEOUT) == std::future_status::ready) { manifest = cache->pending_retrieve.get(); cache->last_fetch = now; cache->recovery_epoch = cur_epoch; } else { // Timed out — future stays in cache, will be checked next call std::cerr << "[CLUSTER-BE] domain=" << _Domain << " retrieve timed out after 5s, using cached data" << std::endl; if (!cache->buffer.empty()) { _Buffer = cache->buffer; _ClusterDataExists.store(_Buffer.size() > sizeof(AuthHeader)); } return; } } catch (const std::exception &e) { std::cerr << "ClusterBackend::fetchFromCluster: retrieve failed: " << e.what() << std::endl; Loading @@ -309,6 +356,7 @@ namespace authdb { _ClusterDataExists.store(true); return; } } if (manifest.size() < sizeof(AuthHeader) + sizeof(uint32_t) + sizeof(uint32_t)) { if (!manifest.empty()) Loading src/backends/cluster.h +2 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ #include <cstdint> #include <atomic> #include <chrono> #include <future> #include <mutex> #include <map> #include <set> Loading @@ -55,6 +56,7 @@ namespace authdb { std::chrono::steady_clock::time_point last_fetch{}; uint64_t recovery_epoch{0}; std::mutex fetch_mutex; std::future<std::vector<uint8_t>> pending_retrieve; }; class VISIBILITY ClusterBackend : public AuthBackendApi { Loading src/cluster.cpp +4 −0 Original line number Diff line number Diff line Loading @@ -544,11 +544,15 @@ namespace authdb { if (health.nodes_online >= n) { std::cerr << "Cluster: all nodes online" << std::endl; critical_ = false; degraded_ = false; return health.nodes_online; } if (health.nodes_online >= k) { std::cerr << "Cluster: DEGRADED — " << health.nodes_online << "/" << n << " nodes online, continuing" << std::endl; critical_ = false; degraded_ = true; return health.nodes_online; } } catch (const std::exception &e) { Loading tests/test_cluster_performance.cpp +61 −21 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ #include <chrono> #include <cstdio> #include <cstring> #include <cstdlib> #include <fstream> #include <iostream> #include <memory> Loading @@ -19,7 +20,20 @@ #include <string> #include <thread> #include <vector> #ifdef _WIN32 #include <process.h> #include <io.h> #include <windows.h> #include <psapi.h> #define test_getpid _getpid #define test_unlink _unlink #else #include <unistd.h> #define test_getpid getpid #define test_unlink unlink #endif #include <uuidp.h> #include "backend.h" Loading Loading @@ -56,6 +70,12 @@ using authdb::RecordIndex; // ═════════════════════════════════════════════════════════════ static size_t getRSSKb() { #ifdef _WIN32 PROCESS_MEMORY_COUNTERS pmc; if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) return pmc.WorkingSetSize / 1024; return 0; #else std::ifstream ifs("/proc/self/status"); std::string line; while (std::getline(ifs, line)) { Loading @@ -66,6 +86,7 @@ static size_t getRSSKb() { } } return 0; #endif } using Clock = std::chrono::high_resolution_clock; Loading Loading @@ -102,8 +123,26 @@ static void printStats(const char *label, const TimingStats &s, size_t n) { static std::string g_certFile; static std::string g_keyFile; static std::string tempDir() { #ifdef _WIN32 const char *tmp = std::getenv("TEMP"); if (!tmp) tmp = std::getenv("TMP"); return tmp ? std::string(tmp) + "\\" : "C:\\Temp\\"; #else return "/tmp/"; #endif } static std::string devNull() { #ifdef _WIN32 return "NUL"; #else return "/dev/null"; #endif } static bool generateTestCerts() { std::string prefix = "/tmp/authdb_cluster_test_" + std::to_string(getpid()); std::string prefix = tempDir() + "authdb_cluster_test_" + std::to_string(test_getpid()); g_keyFile = prefix + ".key"; g_certFile = prefix + ".crt"; Loading @@ -111,7 +150,7 @@ static bool generateTestCerts() { std::string cmd = "openssl req -x509 -newkey rsa:2048 " "-keyout " + g_keyFile + " -out " + g_certFile + " -days 1 -nodes -subj '/CN=authdb-test' -batch 2>/dev/null"; " -days 1 -nodes -subj \"/CN=authdb-test\" -batch 2>" + devNull(); int ret = system(cmd.c_str()); if (ret != 0) { Loading @@ -121,7 +160,7 @@ static bool generateTestCerts() { // Convert key to DER (paritypp/QUIC expects DER private key) std::string derKey = prefix + ".key.der"; cmd = "openssl rsa -in " + g_keyFile + " -outform DER -out " + derKey + " 2>/dev/null"; cmd = "openssl rsa -in " + g_keyFile + " -outform DER -out " + derKey + " 2>" + devNull(); ret = system(cmd.c_str()); if (ret != 0) { std::cerr << "Failed to convert key to DER\n"; Loading @@ -133,14 +172,14 @@ static bool generateTestCerts() { } static void cleanupTestCerts() { if (!g_certFile.empty()) unlink(g_certFile.c_str()); if (!g_keyFile.empty()) unlink(g_keyFile.c_str()); if (!g_certFile.empty()) test_unlink(g_certFile.c_str()); if (!g_keyFile.empty()) test_unlink(g_keyFile.c_str()); // Also clean up PEM key std::string pemKey = g_keyFile; auto pos = pemKey.rfind(".der"); if (pos != std::string::npos) { pemKey = pemKey.substr(0, pos); unlink(pemKey.c_str()); test_unlink(pemKey.c_str()); } } Loading @@ -167,11 +206,11 @@ protected: } // Allocate 3 consecutive ports based on PID int basePort = 14433 + (getpid() % 10000); int basePort = 14433 + (test_getpid() % 10000); for (int i = 0; i < NUM_NODES; ++i) basePorts[i] = basePort + i; fileDbPath = "/tmp/authdb_cluster_perf_" + std::to_string(getpid()) + ".db"; fileDbPath = tempDir() + "authdb_cluster_perf_" + std::to_string(test_getpid()) + ".db"; // Build common peer list (all 3 nodes) std::vector<ClusterNode> allPeers; Loading Loading @@ -226,8 +265,8 @@ protected: clusters[i] = nullptr; } } unlink(fileDbPath.c_str()); unlink((fileDbPath + ".lock").c_str()); test_unlink(fileDbPath.c_str()); test_unlink((fileDbPath + ".lock").c_str()); cleanupTestCerts(); } Loading Loading @@ -389,15 +428,14 @@ TEST_F(ClusterPerformanceTest, GroupCreateReadBenchmark) { // ═════════════════════════════════════════════════════════════ TEST_F(ClusterPerformanceTest, ClusterSessionBenchmark) { // Wait up to 15s for the health monitor to clear the critical flag. // On startup the first node briefly goes CRITICAL before the other // two nodes are reachable; the monitor cycles every 3s in that state. // The waitForPeers() fix now clears critical_ before returning, // so the cluster should be ready. Allow 3s grace for slow CI. if (authdb::g_Cluster) { for (int wait = 0; wait < 15 && authdb::g_Cluster->isCritical(); ++wait) for (int wait = 0; wait < 3 && authdb::g_Cluster->isCritical(); ++wait) std::this_thread::sleep_for(std::chrono::seconds(1)); } if (authdb::g_Cluster && authdb::g_Cluster->isCritical()) GTEST_SKIP() << "Cluster is still critical after 15 s — cannot run session tests"; GTEST_SKIP() << "Cluster is still critical after 3 s — cannot run session tests"; constexpr size_t N = 50; Loading Loading @@ -532,7 +570,7 @@ TEST_F(ClusterPerformanceTest, ClusterVsFileComparison) { std::vector<double> fileReadTimes, clusterReadTimes; // Use a separate file backend for comparison std::string cmpDbPath = "/tmp/authdb_cmp_" + std::to_string(getpid()) + ".db"; std::string cmpDbPath = tempDir() + "authdb_cmp_" + std::to_string(test_getpid()) + ".db"; AuthBackend cmpFile(authdb::File, cmpDbPath, "cmpdomain"); std::vector<uuid::uuid> fids(N), cids(N); Loading Loading @@ -562,7 +600,9 @@ TEST_F(ClusterPerformanceTest, ClusterVsFileComparison) { fileReadTimes.push_back(std::chrono::duration<double, std::micro>(t1 - t0).count()); } // ── Cluster backend ── // ── Cluster backend (fresh domain for fair comparison) ── AuthBackend cmpCluster(authdb::ClusterStore, "", "cmpdomain"); for (size_t i = 0; i < N; ++i) { cids[i].generate(); class authdb::UserData udat(cids[i]); Loading @@ -570,7 +610,7 @@ TEST_F(ClusterPerformanceTest, ClusterVsFileComparison) { udat.setPwHash("pw"); auto t0 = Clock::now(); user.create(*clusterBackend, &udat); user.create(cmpCluster, &udat); auto t1 = Clock::now(); clusterCreateTimes.push_back(std::chrono::duration<double, std::micro>(t1 - t0).count()); } Loading @@ -580,7 +620,7 @@ TEST_F(ClusterPerformanceTest, ClusterVsFileComparison) { size_t pos = sizeof(AuthHeader); auto t0 = Clock::now(); user.info(*clusterBackend, udat, pos); user.info(cmpCluster, udat, pos); auto t1 = Clock::now(); clusterReadTimes.push_back(std::chrono::duration<double, std::micro>(t1 - t0).count()); } Loading @@ -601,6 +641,6 @@ TEST_F(ClusterPerformanceTest, ClusterVsFileComparison) { << " Ratio = " << (cRead.avg_us / std::max(fRead.avg_us, 0.001)) << "x\n"; // Cleanup comparison DB unlink(cmpDbPath.c_str()); unlink((cmpDbPath + ".lock").c_str()); test_unlink(cmpDbPath.c_str()); test_unlink((cmpDbPath + ".lock").c_str()); } tests/test_performance.cpp +34 −3 Original line number Diff line number Diff line Loading @@ -9,13 +9,27 @@ */ #include <gtest/gtest.h> #include <chrono> #include <cstdlib> #include <cstring> #include <fstream> #include <iostream> #include <numeric> #include <string> #include <vector> #ifdef _WIN32 #include <process.h> #include <io.h> #include <windows.h> #include <psapi.h> #define test_getpid _getpid #define test_unlink _unlink #else #include <unistd.h> #define test_getpid getpid #define test_unlink unlink #endif #include <uuidp.h> #include "backend.h" Loading Loading @@ -46,6 +60,12 @@ using authdb::LocalSession; // ═════════════════════════════════════════════════════════════ static size_t getRSSKb() { #ifdef _WIN32 PROCESS_MEMORY_COUNTERS pmc; if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) return pmc.WorkingSetSize / 1024; return 0; #else std::ifstream ifs("/proc/self/status"); std::string line; while (std::getline(ifs, line)) { Loading @@ -56,6 +76,7 @@ static size_t getRSSKb() { } } return 0; #endif } static size_t getFileSize(const std::string &path) { Loading Loading @@ -95,20 +116,30 @@ static void printStats(const char *label, const TimingStats &s, size_t n) { // Fixture // ═════════════════════════════════════════════════════════════ static std::string tempDir() { #ifdef _WIN32 const char *tmp = std::getenv("TEMP"); if (!tmp) tmp = std::getenv("TMP"); return tmp ? std::string(tmp) + "\\" : "C:\\Temp\\"; #else return "/tmp/"; #endif } class PerformanceTest : public ::testing::Test { protected: std::string dbPath; AuthBackend *backend = nullptr; void SetUp() override { dbPath = "/tmp/authdb_perf_" + std::to_string(getpid()) + ".db"; dbPath = tempDir() + "authdb_perf_" + std::to_string(test_getpid()) + ".db"; backend = new AuthBackend(authdb::File, dbPath, "perfdomain"); } void TearDown() override { delete backend; unlink(dbPath.c_str()); unlink((dbPath + ".lock").c_str()); test_unlink(dbPath.c_str()); test_unlink((dbPath + ".lock").c_str()); } }; Loading Loading
src/backends/cluster.cpp +61 −13 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ #include <algorithm> #include <thread> #include <chrono> #include <future> #include <map> #include <tuple> #include <shared_mutex> Loading Loading @@ -294,12 +295,58 @@ namespace authdb { auto &client = g_Cluster->getReadClient(); if (!client) return; // Single retrieve: manifest contains header + magic + entity keys + full record data // Single retrieve with timeout: manifest contains header + magic + entity keys + full record data std::vector<uint8_t> manifest; // Check if a previous timed-out fetch completed in the background if (cache->pending_retrieve.valid()) { auto st = cache->pending_retrieve.wait_for(std::chrono::milliseconds(0)); if (st == std::future_status::ready) { try { manifest = client->retrieve(dgid); manifest = cache->pending_retrieve.get(); cache->last_fetch = now; cache->recovery_epoch = cur_epoch; } catch (const std::exception &e) { std::cerr << "ClusterBackend::fetchFromCluster: background retrieve failed: " << e.what() << std::endl; _ClusterDataExists.store(true); return; } catch (...) { _ClusterDataExists.store(true); return; } } else { // Previous fetch still running — use cached data, don't start another if (!cache->buffer.empty()) { _Buffer = cache->buffer; _ClusterDataExists.store(_Buffer.size() > sizeof(AuthHeader)); } return; } } else { // No pending fetch — start a new async retrieve with timeout try { auto *raw_client = client.get(); cache->pending_retrieve = std::async(std::launch::async, [raw_client, dgid]() { return raw_client->retrieve(dgid); }); static constexpr auto RETRIEVE_TIMEOUT = std::chrono::seconds(5); if (cache->pending_retrieve.wait_for(RETRIEVE_TIMEOUT) == std::future_status::ready) { manifest = cache->pending_retrieve.get(); cache->last_fetch = now; cache->recovery_epoch = cur_epoch; } else { // Timed out — future stays in cache, will be checked next call std::cerr << "[CLUSTER-BE] domain=" << _Domain << " retrieve timed out after 5s, using cached data" << std::endl; if (!cache->buffer.empty()) { _Buffer = cache->buffer; _ClusterDataExists.store(_Buffer.size() > sizeof(AuthHeader)); } return; } } catch (const std::exception &e) { std::cerr << "ClusterBackend::fetchFromCluster: retrieve failed: " << e.what() << std::endl; Loading @@ -309,6 +356,7 @@ namespace authdb { _ClusterDataExists.store(true); return; } } if (manifest.size() < sizeof(AuthHeader) + sizeof(uint32_t) + sizeof(uint32_t)) { if (!manifest.empty()) Loading
src/backends/cluster.h +2 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ #include <cstdint> #include <atomic> #include <chrono> #include <future> #include <mutex> #include <map> #include <set> Loading @@ -55,6 +56,7 @@ namespace authdb { std::chrono::steady_clock::time_point last_fetch{}; uint64_t recovery_epoch{0}; std::mutex fetch_mutex; std::future<std::vector<uint8_t>> pending_retrieve; }; class VISIBILITY ClusterBackend : public AuthBackendApi { Loading
src/cluster.cpp +4 −0 Original line number Diff line number Diff line Loading @@ -544,11 +544,15 @@ namespace authdb { if (health.nodes_online >= n) { std::cerr << "Cluster: all nodes online" << std::endl; critical_ = false; degraded_ = false; return health.nodes_online; } if (health.nodes_online >= k) { std::cerr << "Cluster: DEGRADED — " << health.nodes_online << "/" << n << " nodes online, continuing" << std::endl; critical_ = false; degraded_ = true; return health.nodes_online; } } catch (const std::exception &e) { Loading
tests/test_cluster_performance.cpp +61 −21 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ #include <chrono> #include <cstdio> #include <cstring> #include <cstdlib> #include <fstream> #include <iostream> #include <memory> Loading @@ -19,7 +20,20 @@ #include <string> #include <thread> #include <vector> #ifdef _WIN32 #include <process.h> #include <io.h> #include <windows.h> #include <psapi.h> #define test_getpid _getpid #define test_unlink _unlink #else #include <unistd.h> #define test_getpid getpid #define test_unlink unlink #endif #include <uuidp.h> #include "backend.h" Loading Loading @@ -56,6 +70,12 @@ using authdb::RecordIndex; // ═════════════════════════════════════════════════════════════ static size_t getRSSKb() { #ifdef _WIN32 PROCESS_MEMORY_COUNTERS pmc; if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) return pmc.WorkingSetSize / 1024; return 0; #else std::ifstream ifs("/proc/self/status"); std::string line; while (std::getline(ifs, line)) { Loading @@ -66,6 +86,7 @@ static size_t getRSSKb() { } } return 0; #endif } using Clock = std::chrono::high_resolution_clock; Loading Loading @@ -102,8 +123,26 @@ static void printStats(const char *label, const TimingStats &s, size_t n) { static std::string g_certFile; static std::string g_keyFile; static std::string tempDir() { #ifdef _WIN32 const char *tmp = std::getenv("TEMP"); if (!tmp) tmp = std::getenv("TMP"); return tmp ? std::string(tmp) + "\\" : "C:\\Temp\\"; #else return "/tmp/"; #endif } static std::string devNull() { #ifdef _WIN32 return "NUL"; #else return "/dev/null"; #endif } static bool generateTestCerts() { std::string prefix = "/tmp/authdb_cluster_test_" + std::to_string(getpid()); std::string prefix = tempDir() + "authdb_cluster_test_" + std::to_string(test_getpid()); g_keyFile = prefix + ".key"; g_certFile = prefix + ".crt"; Loading @@ -111,7 +150,7 @@ static bool generateTestCerts() { std::string cmd = "openssl req -x509 -newkey rsa:2048 " "-keyout " + g_keyFile + " -out " + g_certFile + " -days 1 -nodes -subj '/CN=authdb-test' -batch 2>/dev/null"; " -days 1 -nodes -subj \"/CN=authdb-test\" -batch 2>" + devNull(); int ret = system(cmd.c_str()); if (ret != 0) { Loading @@ -121,7 +160,7 @@ static bool generateTestCerts() { // Convert key to DER (paritypp/QUIC expects DER private key) std::string derKey = prefix + ".key.der"; cmd = "openssl rsa -in " + g_keyFile + " -outform DER -out " + derKey + " 2>/dev/null"; cmd = "openssl rsa -in " + g_keyFile + " -outform DER -out " + derKey + " 2>" + devNull(); ret = system(cmd.c_str()); if (ret != 0) { std::cerr << "Failed to convert key to DER\n"; Loading @@ -133,14 +172,14 @@ static bool generateTestCerts() { } static void cleanupTestCerts() { if (!g_certFile.empty()) unlink(g_certFile.c_str()); if (!g_keyFile.empty()) unlink(g_keyFile.c_str()); if (!g_certFile.empty()) test_unlink(g_certFile.c_str()); if (!g_keyFile.empty()) test_unlink(g_keyFile.c_str()); // Also clean up PEM key std::string pemKey = g_keyFile; auto pos = pemKey.rfind(".der"); if (pos != std::string::npos) { pemKey = pemKey.substr(0, pos); unlink(pemKey.c_str()); test_unlink(pemKey.c_str()); } } Loading @@ -167,11 +206,11 @@ protected: } // Allocate 3 consecutive ports based on PID int basePort = 14433 + (getpid() % 10000); int basePort = 14433 + (test_getpid() % 10000); for (int i = 0; i < NUM_NODES; ++i) basePorts[i] = basePort + i; fileDbPath = "/tmp/authdb_cluster_perf_" + std::to_string(getpid()) + ".db"; fileDbPath = tempDir() + "authdb_cluster_perf_" + std::to_string(test_getpid()) + ".db"; // Build common peer list (all 3 nodes) std::vector<ClusterNode> allPeers; Loading Loading @@ -226,8 +265,8 @@ protected: clusters[i] = nullptr; } } unlink(fileDbPath.c_str()); unlink((fileDbPath + ".lock").c_str()); test_unlink(fileDbPath.c_str()); test_unlink((fileDbPath + ".lock").c_str()); cleanupTestCerts(); } Loading Loading @@ -389,15 +428,14 @@ TEST_F(ClusterPerformanceTest, GroupCreateReadBenchmark) { // ═════════════════════════════════════════════════════════════ TEST_F(ClusterPerformanceTest, ClusterSessionBenchmark) { // Wait up to 15s for the health monitor to clear the critical flag. // On startup the first node briefly goes CRITICAL before the other // two nodes are reachable; the monitor cycles every 3s in that state. // The waitForPeers() fix now clears critical_ before returning, // so the cluster should be ready. Allow 3s grace for slow CI. if (authdb::g_Cluster) { for (int wait = 0; wait < 15 && authdb::g_Cluster->isCritical(); ++wait) for (int wait = 0; wait < 3 && authdb::g_Cluster->isCritical(); ++wait) std::this_thread::sleep_for(std::chrono::seconds(1)); } if (authdb::g_Cluster && authdb::g_Cluster->isCritical()) GTEST_SKIP() << "Cluster is still critical after 15 s — cannot run session tests"; GTEST_SKIP() << "Cluster is still critical after 3 s — cannot run session tests"; constexpr size_t N = 50; Loading Loading @@ -532,7 +570,7 @@ TEST_F(ClusterPerformanceTest, ClusterVsFileComparison) { std::vector<double> fileReadTimes, clusterReadTimes; // Use a separate file backend for comparison std::string cmpDbPath = "/tmp/authdb_cmp_" + std::to_string(getpid()) + ".db"; std::string cmpDbPath = tempDir() + "authdb_cmp_" + std::to_string(test_getpid()) + ".db"; AuthBackend cmpFile(authdb::File, cmpDbPath, "cmpdomain"); std::vector<uuid::uuid> fids(N), cids(N); Loading Loading @@ -562,7 +600,9 @@ TEST_F(ClusterPerformanceTest, ClusterVsFileComparison) { fileReadTimes.push_back(std::chrono::duration<double, std::micro>(t1 - t0).count()); } // ── Cluster backend ── // ── Cluster backend (fresh domain for fair comparison) ── AuthBackend cmpCluster(authdb::ClusterStore, "", "cmpdomain"); for (size_t i = 0; i < N; ++i) { cids[i].generate(); class authdb::UserData udat(cids[i]); Loading @@ -570,7 +610,7 @@ TEST_F(ClusterPerformanceTest, ClusterVsFileComparison) { udat.setPwHash("pw"); auto t0 = Clock::now(); user.create(*clusterBackend, &udat); user.create(cmpCluster, &udat); auto t1 = Clock::now(); clusterCreateTimes.push_back(std::chrono::duration<double, std::micro>(t1 - t0).count()); } Loading @@ -580,7 +620,7 @@ TEST_F(ClusterPerformanceTest, ClusterVsFileComparison) { size_t pos = sizeof(AuthHeader); auto t0 = Clock::now(); user.info(*clusterBackend, udat, pos); user.info(cmpCluster, udat, pos); auto t1 = Clock::now(); clusterReadTimes.push_back(std::chrono::duration<double, std::micro>(t1 - t0).count()); } Loading @@ -601,6 +641,6 @@ TEST_F(ClusterPerformanceTest, ClusterVsFileComparison) { << " Ratio = " << (cRead.avg_us / std::max(fRead.avg_us, 0.001)) << "x\n"; // Cleanup comparison DB unlink(cmpDbPath.c_str()); unlink((cmpDbPath + ".lock").c_str()); test_unlink(cmpDbPath.c_str()); test_unlink((cmpDbPath + ".lock").c_str()); }
tests/test_performance.cpp +34 −3 Original line number Diff line number Diff line Loading @@ -9,13 +9,27 @@ */ #include <gtest/gtest.h> #include <chrono> #include <cstdlib> #include <cstring> #include <fstream> #include <iostream> #include <numeric> #include <string> #include <vector> #ifdef _WIN32 #include <process.h> #include <io.h> #include <windows.h> #include <psapi.h> #define test_getpid _getpid #define test_unlink _unlink #else #include <unistd.h> #define test_getpid getpid #define test_unlink unlink #endif #include <uuidp.h> #include "backend.h" Loading Loading @@ -46,6 +60,12 @@ using authdb::LocalSession; // ═════════════════════════════════════════════════════════════ static size_t getRSSKb() { #ifdef _WIN32 PROCESS_MEMORY_COUNTERS pmc; if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) return pmc.WorkingSetSize / 1024; return 0; #else std::ifstream ifs("/proc/self/status"); std::string line; while (std::getline(ifs, line)) { Loading @@ -56,6 +76,7 @@ static size_t getRSSKb() { } } return 0; #endif } static size_t getFileSize(const std::string &path) { Loading Loading @@ -95,20 +116,30 @@ static void printStats(const char *label, const TimingStats &s, size_t n) { // Fixture // ═════════════════════════════════════════════════════════════ static std::string tempDir() { #ifdef _WIN32 const char *tmp = std::getenv("TEMP"); if (!tmp) tmp = std::getenv("TMP"); return tmp ? std::string(tmp) + "\\" : "C:\\Temp\\"; #else return "/tmp/"; #endif } class PerformanceTest : public ::testing::Test { protected: std::string dbPath; AuthBackend *backend = nullptr; void SetUp() override { dbPath = "/tmp/authdb_perf_" + std::to_string(getpid()) + ".db"; dbPath = tempDir() + "authdb_perf_" + std::to_string(test_getpid()) + ".db"; backend = new AuthBackend(authdb::File, dbPath, "perfdomain"); } void TearDown() override { delete backend; unlink(dbPath.c_str()); unlink((dbPath + ".lock").c_str()); test_unlink(dbPath.c_str()); test_unlink((dbPath + ".lock").c_str()); } }; Loading