Loading build_output.log 0 → 100644 +16 −0 Original line number Diff line number Diff line [1/16] Building CXX object CMakeFiles/mediadb_test.dir/src/cluster.cpp.o [2/16] Building CXX object CMakeFiles/mediadb_perf_test.dir/src/cluster.cpp.o [3/16] Building CXX object CMakeFiles/mediadb.dir/src/cluster.cpp.o [4/16] Building CXX object CMakeFiles/mediadb_cluster_perf_test.dir/src/cluster.cpp.o [5/16] Building CXX object CMakeFiles/mediadb.dir/src/main.cpp.o [6/16] Building CXX object CMakeFiles/mediadb.dir/src/server.cpp.o [7/16] Building CXX object CMakeFiles/mediadb_cluster_perf_test.dir/test/mediadb_cluster_perf_test.cpp.o [8/16] Building CXX object CMakeFiles/mediadb_perf_test.dir/src/backend.cpp.o [9/16] Building CXX object CMakeFiles/mediadb_test.dir/src/backend.cpp.o [10/16] Building CXX object CMakeFiles/mediadb.dir/src/backend.cpp.o [11/16] Building CXX object CMakeFiles/mediadb.dir/src/app.cpp.o [12/16] Linking CXX executable mediadb_perf_test [13/16] Linking CXX executable mediadb_test [14/16] Building CXX object CMakeFiles/mediadb_cluster_perf_test.dir/src/backend.cpp.o [15/16] Linking CXX executable mediadb [16/16] Linking CXX executable mediadb_cluster_perf_test debian/changelog +5 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,11 @@ mediadb (20260422+59) unstable; urgency=low * Fix stuck import state: clean up orphaned H2 import sessions on client disconnect so import_running_ is correctly reset. * Show import status (In Progress / Idle) on cluster status page. * Fix false DEGRADED during import: health probe retries once before counting a peer as offline; suppress DEGRADED transition while import_active_ is set so busy peers are not marked as down. * Notify Cluster of import state via setImportRunning() so the health loop can tolerate transient probe failures during replication. -- Jan Koester <jan.koester@tuxist.de> Wed, 22 Apr 2026 00:00:00 +0200 Loading src/app.cpp +4 −0 Original line number Diff line number Diff line Loading @@ -448,6 +448,7 @@ HttpResponse App::start_import(const std::uint8_t* data, std::size_t len) { import_ok_.store(false); import_error_.clear(); } if (g_Cluster) g_Cluster->setImportRunning(true); std::cerr << "[import] starting synchronous import (" << len << " bytes)" << "\n"; bool ok = db_.import_db_from_buffer(data, len); Loading Loading @@ -477,6 +478,7 @@ HttpResponse App::start_import_file(const std::string& path) { import_ok_.store(false); import_error_.clear(); } if (g_Cluster) g_Cluster->setImportRunning(true); std::cerr << "[import] starting file-based import from " << path << "\n"; bool ok = db_.import_db(path); Loading Loading @@ -510,6 +512,7 @@ std::unique_ptr<ImportSession> App::begin_import() { import_ok_.store(false); import_error_.clear(); } if (g_Cluster) g_Cluster->setImportRunning(true); auto session = db_.begin_import(); if (!session) { std::cerr << "[import] begin_import: db_.begin_import() returned null\n"; Loading @@ -525,6 +528,7 @@ void App::finish_import(bool ok, const std::string& error) { import_ok_.store(ok); if (!ok && !error.empty()) import_error_ = error; import_running_.store(false); if (g_Cluster) g_Cluster->setImportRunning(false); } // ---- main router ---- Loading src/cluster.cpp +24 −5 Original line number Diff line number Diff line Loading @@ -500,9 +500,17 @@ void Cluster::health_loop() { continue; } std::vector<uint64_t> dummy; if (pclient_->list_groups_on_node(it->second, dummy)) { ++online; } else { // Retry once after short delay — peers may be busy // during import or scrub and miss the first probe std::this_thread::sleep_for(std::chrono::milliseconds(500)); dummy.clear(); if (pclient_->list_groups_on_node(it->second, dummy)) ++online; } } server_->set_peer_status_callback([online, total]() -> std::pair<uint32_t, uint32_t> { return {online, total}; }); Loading Loading @@ -562,11 +570,22 @@ void Cluster::health_loop() { }).detach(); } } else if (online >= k) { // During active imports, peers may be busy handling // replication traffic and miss health probes. Suppress // the DEGRADED transition so it doesn't trigger a // recovery-scrub when the import finishes normally. if (!import_active_.load()) { if (!degraded_) { std::cerr << "[CLUSTER] DEGRADED — " << online << "/" << n << " nodes online\n"; } degraded_ = true; } else { if (!degraded_) { std::cerr << "[CLUSTER] probe missed " << (n - online) << " node(s)" << " during import — suppressing DEGRADED\n"; } } critical_ = false; healthy_cycles = 0; } else { Loading src/cluster.h +7 −0 Original line number Diff line number Diff line Loading @@ -176,11 +176,18 @@ public: bool isScrubRunning() const { return scrub_running_.load(); } // External flag: set by App when an import is in progress, so the // health loop can tolerate transient probe failures (peers busy // handling import replication traffic). void setImportRunning(bool v) { import_active_.store(v); } bool isImportRunning() const { return import_active_.load(); } private: ClusterConfig cfg_; std::atomic<bool> running_{false}; std::atomic<bool> degraded_{false}; std::atomic<bool> critical_{false}; std::atomic<bool> import_active_{false}; std::thread server_thread_; std::thread health_thread_; std::mutex scrub_mutex_; Loading Loading
build_output.log 0 → 100644 +16 −0 Original line number Diff line number Diff line [1/16] Building CXX object CMakeFiles/mediadb_test.dir/src/cluster.cpp.o [2/16] Building CXX object CMakeFiles/mediadb_perf_test.dir/src/cluster.cpp.o [3/16] Building CXX object CMakeFiles/mediadb.dir/src/cluster.cpp.o [4/16] Building CXX object CMakeFiles/mediadb_cluster_perf_test.dir/src/cluster.cpp.o [5/16] Building CXX object CMakeFiles/mediadb.dir/src/main.cpp.o [6/16] Building CXX object CMakeFiles/mediadb.dir/src/server.cpp.o [7/16] Building CXX object CMakeFiles/mediadb_cluster_perf_test.dir/test/mediadb_cluster_perf_test.cpp.o [8/16] Building CXX object CMakeFiles/mediadb_perf_test.dir/src/backend.cpp.o [9/16] Building CXX object CMakeFiles/mediadb_test.dir/src/backend.cpp.o [10/16] Building CXX object CMakeFiles/mediadb.dir/src/backend.cpp.o [11/16] Building CXX object CMakeFiles/mediadb.dir/src/app.cpp.o [12/16] Linking CXX executable mediadb_perf_test [13/16] Linking CXX executable mediadb_test [14/16] Building CXX object CMakeFiles/mediadb_cluster_perf_test.dir/src/backend.cpp.o [15/16] Linking CXX executable mediadb [16/16] Linking CXX executable mediadb_cluster_perf_test
debian/changelog +5 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,11 @@ mediadb (20260422+59) unstable; urgency=low * Fix stuck import state: clean up orphaned H2 import sessions on client disconnect so import_running_ is correctly reset. * Show import status (In Progress / Idle) on cluster status page. * Fix false DEGRADED during import: health probe retries once before counting a peer as offline; suppress DEGRADED transition while import_active_ is set so busy peers are not marked as down. * Notify Cluster of import state via setImportRunning() so the health loop can tolerate transient probe failures during replication. -- Jan Koester <jan.koester@tuxist.de> Wed, 22 Apr 2026 00:00:00 +0200 Loading
src/app.cpp +4 −0 Original line number Diff line number Diff line Loading @@ -448,6 +448,7 @@ HttpResponse App::start_import(const std::uint8_t* data, std::size_t len) { import_ok_.store(false); import_error_.clear(); } if (g_Cluster) g_Cluster->setImportRunning(true); std::cerr << "[import] starting synchronous import (" << len << " bytes)" << "\n"; bool ok = db_.import_db_from_buffer(data, len); Loading Loading @@ -477,6 +478,7 @@ HttpResponse App::start_import_file(const std::string& path) { import_ok_.store(false); import_error_.clear(); } if (g_Cluster) g_Cluster->setImportRunning(true); std::cerr << "[import] starting file-based import from " << path << "\n"; bool ok = db_.import_db(path); Loading Loading @@ -510,6 +512,7 @@ std::unique_ptr<ImportSession> App::begin_import() { import_ok_.store(false); import_error_.clear(); } if (g_Cluster) g_Cluster->setImportRunning(true); auto session = db_.begin_import(); if (!session) { std::cerr << "[import] begin_import: db_.begin_import() returned null\n"; Loading @@ -525,6 +528,7 @@ void App::finish_import(bool ok, const std::string& error) { import_ok_.store(ok); if (!ok && !error.empty()) import_error_ = error; import_running_.store(false); if (g_Cluster) g_Cluster->setImportRunning(false); } // ---- main router ---- Loading
src/cluster.cpp +24 −5 Original line number Diff line number Diff line Loading @@ -500,9 +500,17 @@ void Cluster::health_loop() { continue; } std::vector<uint64_t> dummy; if (pclient_->list_groups_on_node(it->second, dummy)) { ++online; } else { // Retry once after short delay — peers may be busy // during import or scrub and miss the first probe std::this_thread::sleep_for(std::chrono::milliseconds(500)); dummy.clear(); if (pclient_->list_groups_on_node(it->second, dummy)) ++online; } } server_->set_peer_status_callback([online, total]() -> std::pair<uint32_t, uint32_t> { return {online, total}; }); Loading Loading @@ -562,11 +570,22 @@ void Cluster::health_loop() { }).detach(); } } else if (online >= k) { // During active imports, peers may be busy handling // replication traffic and miss health probes. Suppress // the DEGRADED transition so it doesn't trigger a // recovery-scrub when the import finishes normally. if (!import_active_.load()) { if (!degraded_) { std::cerr << "[CLUSTER] DEGRADED — " << online << "/" << n << " nodes online\n"; } degraded_ = true; } else { if (!degraded_) { std::cerr << "[CLUSTER] probe missed " << (n - online) << " node(s)" << " during import — suppressing DEGRADED\n"; } } critical_ = false; healthy_cycles = 0; } else { Loading
src/cluster.h +7 −0 Original line number Diff line number Diff line Loading @@ -176,11 +176,18 @@ public: bool isScrubRunning() const { return scrub_running_.load(); } // External flag: set by App when an import is in progress, so the // health loop can tolerate transient probe failures (peers busy // handling import replication traffic). void setImportRunning(bool v) { import_active_.store(v); } bool isImportRunning() const { return import_active_.load(); } private: ClusterConfig cfg_; std::atomic<bool> running_{false}; std::atomic<bool> degraded_{false}; std::atomic<bool> critical_{false}; std::atomic<bool> import_active_{false}; std::thread server_thread_; std::thread health_thread_; std::mutex scrub_mutex_; Loading