Commit b949e037 authored by jan.koester's avatar jan.koester
Browse files

test

parent 789cfa65
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
mediadb (20260419+27) unstable; urgency=critical

  * Fix catastrophic cluster data loss ("stores disappeared") caused by nodes 
    pushing an empty local memory state to the global index when network fetches 
    timed out. Replication loops (`repair_replication` and edits) are now 
    forcefully blocked (`HTTP 503`/`std::runtime_error`) if a node has not yet 
    successfully completed an initial sync (`initial_sync_ok_`). 

 -- Jan Koester <jan.koester@tuxist.de>  Sun, 19 Apr 2026 15:30:00 +0200

mediadb (20260419+26) unstable; urgency=critical

  * Fix preview std::future_error exception caused by invalidating future read states. 
+23 −0
Original line number Diff line number Diff line
@@ -2489,6 +2489,9 @@ std::vector<StoreRecord> ClusterMediaBackend::list_stores() const {
}

bool ClusterMediaBackend::delete_store(const std::string& id) {
    if (!initial_sync_ok_.load()) {
        throw std::runtime_error("Cluster node isolated or syncing. Try again later.");
    }
    std::unique_lock<std::shared_mutex> cguard(cluster_op_mutex_);
    
    // Collect all media and albums to remove their cluster objects
@@ -2604,6 +2607,9 @@ std::vector<AlbumRecord> ClusterMediaBackend::list_albums(const std::string& sto
}

bool ClusterMediaBackend::delete_album(const std::string& id) {
    if (!initial_sync_ok_.load()) {
        throw std::runtime_error("Cluster node isolated or syncing. Try again later.");
    }
    std::unique_lock<std::shared_mutex> cguard(cluster_op_mutex_);
    auto album = local_.get_album(id);
    if (!album) return false;
@@ -2681,6 +2687,9 @@ std::vector<MediaRecord> ClusterMediaBackend::list_media(const std::string& albu
}

bool ClusterMediaBackend::delete_media(const std::string& id) {
    if (!initial_sync_ok_.load()) {
        throw std::runtime_error("Cluster node isolated or syncing. Try again later.");
    }
    std::unique_lock<std::shared_mutex> cguard(cluster_op_mutex_);
    auto media = local_.get_media(id);
    if (!media) return false;
@@ -2976,13 +2985,27 @@ void ClusterMediaBackend::sync_from_cluster() {
        return;
    }

    uint64_t index_gid = cluster_group_id("index");
    bool has_index = false;
    for (const auto& pg : cluster_.list_peer_groups()) {
        if (std::find(pg.groups.begin(), pg.groups.end(), index_gid) != pg.groups.end()) {
            has_index = true;
            break;
        }
    }

    // Fetch index from cluster (contains store/album/ACL metadata, no media data)
    std::vector<uint8_t> index_data;
    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";
            initial_sync_ok_.store(true);
        } else {
        std::cerr << "[CLUSTER-SYNC] index fetch "
                  << (fetch_ok ? "returned empty data" : "failed (exception in cluster layer)")
                  << "\n";
        }
        return;
    }
    {

src/backend.cpp.rej

0 → 100644
+12 −0
Original line number Diff line number Diff line
--- src/backend.cpp
+++ src/backend.cpp
@@ -2578,6 +2578,9 @@
 
 std::optional<AlbumRecord> ClusterMediaBackend::create_album(const std::string& store_id, const std::string& name) {
+    if (!initial_sync_ok_.load()) {
+        throw std::runtime_error("Cluster node isolated or syncing. Try again later.");
+    }
     std::unique_lock<std::shared_mutex> cguard(cluster_op_mutex_);
     auto a = local_.create_album(store_id, name);
     if (a) replicate_store(store_id);
 
+1 −0
Original line number Diff line number Diff line
@@ -337,6 +337,7 @@ private:
    Cluster& cluster_;
    std::atomic<bool> sync_running_{false};
    std::atomic<bool> importing_{false};
    std::atomic<bool> initial_sync_ok_{false};
    std::thread sync_thread_;
    std::mutex sync_mutex_;
    std::condition_variable sync_cv_;