Commit 4ca814ef authored by jan.koester's avatar jan.koester
Browse files

test

parent 01594b7a
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
mediadb (20260419+22) unstable; urgency=critical

  * Fix Cluster Media Import data loss / corruption: The import_db_from_buffer
    sync loop in ClusterMediaBackend now halts replication immediately (after
    up to 3 retries) if a media chunk fails to replicate over the network.
    Previously, it would silently ignore failed chunks and continue committing
    store:id and index blobs, leaving orphan keys on peer nodes ("nicht auf 
    allen nodes heile").

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

mediadb (20260419+21) unstable; urgency=low

  * Fix delete_store in ClusterMediaBackend to properly delete all associated media blobs from cluster storage.
+18 −35
Original line number Diff line number Diff line
@@ -2859,51 +2859,34 @@ bool ClusterMediaBackend::import_db_from_buffer(const std::uint8_t* data, std::s
    // cluster_op_mutex_ is still held so ClusterMediaBackend reads wait,
    // but sync cannot interfere.
    DBG_LOG("[CLUSTER-IMPORT] Phase 2: replicating " << repl_queue.size() << " buffers\n");
    std::vector<std::pair<std::string, std::vector<uint8_t>>> failed_entries;
    int repl_idx = 0;
    for (auto& [key, buf] : repl_queue) {
        ++repl_idx;
        DBG_LOG("[CLUSTER-IMPORT]   replicate " << repl_idx << "/" << repl_queue.size()
                  << " key=" << key << " size=" << buf.size() << "\n");
        if (!cluster_.replicate(key, buf.data(), buf.size())) {
            DBG_LOG("[CLUSTER-IMPORT]   FAILED key=" << key << "\n");
            failed_entries.emplace_back(std::move(key), std::move(buf));
        } else {
            DBG_LOG("[CLUSTER-IMPORT]   OK key=" << key << "\n");
            buf.clear();
            buf.shrink_to_fit();
        }
    }
    repl_queue.clear();
        
    // Phase 3: Retry failures (using the original buffers kept from Phase 2)
    if (!failed_entries.empty()) {
        DBG_LOG("[CLUSTER-IMPORT] Phase 3: retrying " << failed_entries.size() << " failed keys\n");
        bool ok = false;
        constexpr int MAX_RETRIES = 3;
        for (int attempt = 1; attempt <= MAX_RETRIES && !failed_entries.empty(); ++attempt) {
            DBG_LOG("[CLUSTER-IMPORT]   retry attempt " << attempt << "\n");
        for (int attempt = 1; attempt <= MAX_RETRIES; ++attempt) {
            if (cluster_.replicate(key, buf.data(), buf.size())) {
                ok = true;
                break;
            }
            DBG_LOG("[CLUSTER-IMPORT]   retry attempt " << attempt << " for key=" << key << "\n");
            std::this_thread::sleep_for(std::chrono::seconds(attempt * 2));
            std::vector<std::pair<std::string, std::vector<uint8_t>>> still_failed;
            for (auto& [key, buf] : failed_entries) {
                bool ok = false;
                if (key == "index") {
                    // Re-generate index buffer (cheap)
                    auto idx_buf = local_.save_index_to_buffer();
                    ok = cluster_.replicate("index", idx_buf.data(), idx_buf.size());
                } else {
                    // store: and media: keys — use the original buffer
                    ok = cluster_.replicate(key, buf.data(), buf.size());
        }

        if (!ok) {
                    still_failed.emplace_back(std::move(key), std::move(buf));
                }
            }
            failed_entries = std::move(still_failed);
            if (failed_entries.empty()) {
                break;
            }
            DBG_LOG("[CLUSTER-IMPORT]   FAILED key=" << key << " after retries. Aborting import.\n");
            importing_.store(false);
            return false;
        }

        DBG_LOG("[CLUSTER-IMPORT]   OK key=" << key << "\n");
        buf.clear();
        buf.shrink_to_fit();
    }
    repl_queue.clear();

    DBG_LOG("[CLUSTER-IMPORT] complete, success\n");
    importing_.store(false);