Loading debian/changelog +9 −0 Original line number Diff line number Diff line mediadb (20260419+28) unstable; urgency=critical * Fix cluster startup deadlock: initial_sync_ok_ was only set on empty clusters but never after a successful index fetch, causing all nodes with real data to remain permanently locked in "syncing" state (5/5 incomplete). Now properly set after load_index_from_buffer succeeds. -- Jan Koester <jan.koester@tuxist.de> Sun, 19 Apr 2026 16:15:00 +0200 mediadb (20260419+27) unstable; urgency=critical * Fix catastrophic cluster data loss ("stores disappeared") caused by nodes Loading src/backend.cpp +13 −14 Original line number Diff line number Diff line Loading @@ -2436,18 +2436,15 @@ void ClusterMediaBackend::start_sync() { // to become fully available even after waitForPeers returns. for (int attempt = 1; attempt <= 5; ++attempt) { sync_from_cluster(); { if (initial_sync_ok_.load()) { std::shared_lock<std::shared_mutex> cguard(cluster_op_mutex_); auto sids = local_.store_ids(); if (!sids.empty()) { // Count total albums across all stores std::size_t total_albums = 0; for (const auto& sid : sids) total_albums += local_.list_albums(sid).size(); std::cerr << "[CLUSTER-SYNC] initial sync OK on attempt " << attempt << ": " << sids.size() << " stores, " << total_albums << " albums\n"; if (total_albums > 0) break; // fully synced } break; } std::cerr << "[CLUSTER-SYNC] initial sync attempt " << attempt << "/5 incomplete, retrying in 2s\n"; Loading Loading @@ -2999,19 +2996,21 @@ void ClusterMediaBackend::sync_from_cluster() { bool fetch_ok = cluster_.fetch("index", index_data); if (!fetch_ok || index_data.empty()) { if (!has_index) { std::cerr << "[CLUSTER-SYNC] fresh empty cluster detected\n"; std::cerr << "[CLUSTER-SYNC] no index exists on any peer — fresh/empty cluster\n"; initial_sync_ok_.store(true); } else { std::cerr << "[CLUSTER-SYNC] index fetch " << (fetch_ok ? "returned empty data" : "failed (exception in cluster layer)") << "\n"; << " (has_index=true, peers report having group)\n"; } return; } std::cerr << "[CLUSTER-SYNC] index fetched OK, " << index_data.size() << " bytes\n"; { std::unique_lock<std::shared_mutex> cguard(cluster_op_mutex_); local_.load_index_from_buffer(index_data); } initial_sync_ok_.store(true); // Snapshot store IDs under shared lock std::vector<std::string> sids; Loading Loading
debian/changelog +9 −0 Original line number Diff line number Diff line mediadb (20260419+28) unstable; urgency=critical * Fix cluster startup deadlock: initial_sync_ok_ was only set on empty clusters but never after a successful index fetch, causing all nodes with real data to remain permanently locked in "syncing" state (5/5 incomplete). Now properly set after load_index_from_buffer succeeds. -- Jan Koester <jan.koester@tuxist.de> Sun, 19 Apr 2026 16:15:00 +0200 mediadb (20260419+27) unstable; urgency=critical * Fix catastrophic cluster data loss ("stores disappeared") caused by nodes Loading
src/backend.cpp +13 −14 Original line number Diff line number Diff line Loading @@ -2436,18 +2436,15 @@ void ClusterMediaBackend::start_sync() { // to become fully available even after waitForPeers returns. for (int attempt = 1; attempt <= 5; ++attempt) { sync_from_cluster(); { if (initial_sync_ok_.load()) { std::shared_lock<std::shared_mutex> cguard(cluster_op_mutex_); auto sids = local_.store_ids(); if (!sids.empty()) { // Count total albums across all stores std::size_t total_albums = 0; for (const auto& sid : sids) total_albums += local_.list_albums(sid).size(); std::cerr << "[CLUSTER-SYNC] initial sync OK on attempt " << attempt << ": " << sids.size() << " stores, " << total_albums << " albums\n"; if (total_albums > 0) break; // fully synced } break; } std::cerr << "[CLUSTER-SYNC] initial sync attempt " << attempt << "/5 incomplete, retrying in 2s\n"; Loading Loading @@ -2999,19 +2996,21 @@ void ClusterMediaBackend::sync_from_cluster() { bool fetch_ok = cluster_.fetch("index", index_data); if (!fetch_ok || index_data.empty()) { if (!has_index) { std::cerr << "[CLUSTER-SYNC] fresh empty cluster detected\n"; std::cerr << "[CLUSTER-SYNC] no index exists on any peer — fresh/empty cluster\n"; initial_sync_ok_.store(true); } else { std::cerr << "[CLUSTER-SYNC] index fetch " << (fetch_ok ? "returned empty data" : "failed (exception in cluster layer)") << "\n"; << " (has_index=true, peers report having group)\n"; } return; } std::cerr << "[CLUSTER-SYNC] index fetched OK, " << index_data.size() << " bytes\n"; { std::unique_lock<std::shared_mutex> cguard(cluster_op_mutex_); local_.load_index_from_buffer(index_data); } initial_sync_ok_.store(true); // Snapshot store IDs under shared lock std::vector<std::string> sids; Loading