Loading src/backend.cpp +20 −20 Original line number Diff line number Diff line Loading @@ -3053,18 +3053,13 @@ bool ClusterMediaBackend::import_db_from_buffer(const std::uint8_t* data, std::s << " replicated=" << repl_count << " failed=" << repl_fail << "\n"; if (!parse_ok || repl_fail > 0) return false; // Clear tombstones for imported stores { auto sids = local_.store_ids(); bool tombstone_changed = false; for (const auto& sid : sids) { if (tombstones_.erase(sid) > 0) tombstone_changed = true; } if (tombstone_changed) { // Clear ALL tombstones after successful import. A full import replaces // the entire dataset — any previous delete tombstones are stale and must // not suppress the newly imported stores on other nodes. if (!tombstones_.empty()) { tombstones_.clear(); replicate_tombstones(); std::cerr << "[CLUSTER-IMPORT] cleared tombstones\n"; } std::cerr << "[CLUSTER-IMPORT] cleared all tombstones\n"; } return true; Loading Loading @@ -3143,14 +3138,13 @@ std::unique_ptr<ImportSession> ClusterMediaBackend::begin_import() { : inner_(std::move(inner)), flag_(flag), backend_(backend) {} ~ImportGuardSession() override { if (ok()) { auto sids = backend_.local_.store_ids(); bool tombstone_changed = false; for (const auto& sid : sids) { if (backend_.tombstones_.erase(sid) > 0) tombstone_changed = true; } if (tombstone_changed) // Clear ALL tombstones after successful import. Any previous // delete tombstones are stale and must not suppress the newly // imported stores on other nodes during sync. if (!backend_.tombstones_.empty()) { backend_.tombstones_.clear(); backend_.replicate_tombstones(); } flag_.store(false); // Sync from cluster to pick up the replicated data on this node try { backend_.sync_from_cluster(); } catch (...) {} Loading Loading @@ -3541,14 +3535,20 @@ void ClusterMediaBackend::load_tombstones() { pos += 4; return v; }; // REPLACE local tombstones with cluster tombstones (not merge). // Merging caused a loop: deleted tombstones were re-inserted from // the cluster on every sync, permanently deleting stores that // a successful import had already cleared. std::set<std::string> cluster_tombstones; auto count = read_u32(); for (std::uint32_t i = 0; i < count && pos < buf.size(); ++i) { auto len = read_u32(); if (pos + len > buf.size()) break; std::string id(buf.begin() + pos, buf.begin() + pos + len); pos += len; tombstones_.insert(std::move(id)); cluster_tombstones.insert(std::move(id)); } tombstones_ = std::move(cluster_tombstones); } catch (...) { // No tombstones blob yet — that's fine } Loading Loading
src/backend.cpp +20 −20 Original line number Diff line number Diff line Loading @@ -3053,18 +3053,13 @@ bool ClusterMediaBackend::import_db_from_buffer(const std::uint8_t* data, std::s << " replicated=" << repl_count << " failed=" << repl_fail << "\n"; if (!parse_ok || repl_fail > 0) return false; // Clear tombstones for imported stores { auto sids = local_.store_ids(); bool tombstone_changed = false; for (const auto& sid : sids) { if (tombstones_.erase(sid) > 0) tombstone_changed = true; } if (tombstone_changed) { // Clear ALL tombstones after successful import. A full import replaces // the entire dataset — any previous delete tombstones are stale and must // not suppress the newly imported stores on other nodes. if (!tombstones_.empty()) { tombstones_.clear(); replicate_tombstones(); std::cerr << "[CLUSTER-IMPORT] cleared tombstones\n"; } std::cerr << "[CLUSTER-IMPORT] cleared all tombstones\n"; } return true; Loading Loading @@ -3143,14 +3138,13 @@ std::unique_ptr<ImportSession> ClusterMediaBackend::begin_import() { : inner_(std::move(inner)), flag_(flag), backend_(backend) {} ~ImportGuardSession() override { if (ok()) { auto sids = backend_.local_.store_ids(); bool tombstone_changed = false; for (const auto& sid : sids) { if (backend_.tombstones_.erase(sid) > 0) tombstone_changed = true; } if (tombstone_changed) // Clear ALL tombstones after successful import. Any previous // delete tombstones are stale and must not suppress the newly // imported stores on other nodes during sync. if (!backend_.tombstones_.empty()) { backend_.tombstones_.clear(); backend_.replicate_tombstones(); } flag_.store(false); // Sync from cluster to pick up the replicated data on this node try { backend_.sync_from_cluster(); } catch (...) {} Loading Loading @@ -3541,14 +3535,20 @@ void ClusterMediaBackend::load_tombstones() { pos += 4; return v; }; // REPLACE local tombstones with cluster tombstones (not merge). // Merging caused a loop: deleted tombstones were re-inserted from // the cluster on every sync, permanently deleting stores that // a successful import had already cleared. std::set<std::string> cluster_tombstones; auto count = read_u32(); for (std::uint32_t i = 0; i < count && pos < buf.size(); ++i) { auto len = read_u32(); if (pos + len > buf.size()) break; std::string id(buf.begin() + pos, buf.begin() + pos + len); pos += len; tombstones_.insert(std::move(id)); cluster_tombstones.insert(std::move(id)); } tombstones_ = std::move(cluster_tombstones); } catch (...) { // No tombstones blob yet — that's fine } Loading