Loading debian/changelog +10 −0 Original line number Diff line number Diff line mediadb (20260419+23) unstable; urgency=critical * Fix Cluster Media Import: Always upload media data blobs ("media:<id>") to cluster layer during .mdb imports, even if the node already knows about this file in its index. This allows re-importing .mdb archives to successfully act as a self-healing repair ("heile machen") for nodes with missing/broken stripes (which previously skipped uploading and thus broke preview/playback). -- Jan Koester <jan.koester@tuxist.de> Sun, 19 Apr 2026 12:45:00 +0200 mediadb (20260419+22) unstable; urgency=critical * Fix Cluster Media Import data loss / corruption: The import_db_from_buffer Loading src/backend.cpp +28 −25 Original line number Diff line number Diff line Loading @@ -777,8 +777,11 @@ bool BinDb::import_from_stream(std::istream& in, bin_write_str(store_buf, created_at); bin_write_u64(store_buf, data_size); if (data_size > 0 && new_media) { // Read media data and replicate as separate key: media:<id> if (data_size > 0) { // In cluster mode, ALWAYS read and replicate media data: // This allows re-importing an .mdb file to self-heal missing // stripes and orphan blobs on nodes (e.g. from previous failures) // even if the metadata itself is already present. std::vector<std::uint8_t> media_data(data_size); in.read(reinterpret_cast<char*>(media_data.data()), static_cast<std::streamsize>(data_size)); Loading @@ -786,9 +789,6 @@ bool BinDb::import_from_stream(std::istream& in, if (got < data_size) media_data.resize(got); on_replicate("media:" + media_id, media_data.data(), media_data.size()); } else if (data_size > 0) { // Existing media: skip past raw data in.seekg(static_cast<std::streamoff>(data_size), std::ios::cur); } // Register metadata only (no raw data in RAM) Loading Loading @@ -1280,13 +1280,15 @@ private: size_t n = std::min(avail, remaining); if (n == 0) return; if (cur_media_new_) { if (stream_to_cluster_) { // Accumulate media binary data separately for "media:<id>" key // In cluster mode, ALWAYS read and replicate media data: // This allows re-importing an .mdb file to self-heal missing // stripes and orphan blobs on nodes. media_data_buf_.insert(media_data_buf_.end(), buf_.data() + pos_, buf_.data() + pos_ + n); } else if (store_out_.is_open()) { } else if (cur_media_new_) { if (store_out_.is_open()) { // Write directly to store file store_out_.write(buf_.data() + pos_, static_cast<std::streamsize>(n)); if (!store_out_.good()) { Loading @@ -1301,7 +1303,7 @@ private: buf_.data() + pos_ + n); } } // Skip data for existing media (just advance pos_) // Advance pos_ pos_ += n; data_written_ += n; } Loading @@ -1324,12 +1326,13 @@ private: db_.media_[mid] = std::move(*pending_media_); db_.albums_[aid].media_ids.push_back(mid); pending_media_.reset(); } // Cluster: replicate binary data as separate "media:<id>" key if (stream_to_cluster_ && !media_data_buf_.empty()) { auto data = std::make_shared<std::vector<uint8_t>>( std::move(media_data_buf_)); std::string key = "media:" + mid; std::string key = "media:" + cur_media_id_; { std::lock_guard<std::mutex> dlk(deferred_mutex_); pending_ops_.push_back([this, key, data]() { Loading @@ -1338,7 +1341,7 @@ private: } } media_data_buf_.clear(); } ++mi_; if (mi_ < num_media_) { phase_ = Phase::MEDIA_HEADER; Loading Loading
debian/changelog +10 −0 Original line number Diff line number Diff line mediadb (20260419+23) unstable; urgency=critical * Fix Cluster Media Import: Always upload media data blobs ("media:<id>") to cluster layer during .mdb imports, even if the node already knows about this file in its index. This allows re-importing .mdb archives to successfully act as a self-healing repair ("heile machen") for nodes with missing/broken stripes (which previously skipped uploading and thus broke preview/playback). -- Jan Koester <jan.koester@tuxist.de> Sun, 19 Apr 2026 12:45:00 +0200 mediadb (20260419+22) unstable; urgency=critical * Fix Cluster Media Import data loss / corruption: The import_db_from_buffer Loading
src/backend.cpp +28 −25 Original line number Diff line number Diff line Loading @@ -777,8 +777,11 @@ bool BinDb::import_from_stream(std::istream& in, bin_write_str(store_buf, created_at); bin_write_u64(store_buf, data_size); if (data_size > 0 && new_media) { // Read media data and replicate as separate key: media:<id> if (data_size > 0) { // In cluster mode, ALWAYS read and replicate media data: // This allows re-importing an .mdb file to self-heal missing // stripes and orphan blobs on nodes (e.g. from previous failures) // even if the metadata itself is already present. std::vector<std::uint8_t> media_data(data_size); in.read(reinterpret_cast<char*>(media_data.data()), static_cast<std::streamsize>(data_size)); Loading @@ -786,9 +789,6 @@ bool BinDb::import_from_stream(std::istream& in, if (got < data_size) media_data.resize(got); on_replicate("media:" + media_id, media_data.data(), media_data.size()); } else if (data_size > 0) { // Existing media: skip past raw data in.seekg(static_cast<std::streamoff>(data_size), std::ios::cur); } // Register metadata only (no raw data in RAM) Loading Loading @@ -1280,13 +1280,15 @@ private: size_t n = std::min(avail, remaining); if (n == 0) return; if (cur_media_new_) { if (stream_to_cluster_) { // Accumulate media binary data separately for "media:<id>" key // In cluster mode, ALWAYS read and replicate media data: // This allows re-importing an .mdb file to self-heal missing // stripes and orphan blobs on nodes. media_data_buf_.insert(media_data_buf_.end(), buf_.data() + pos_, buf_.data() + pos_ + n); } else if (store_out_.is_open()) { } else if (cur_media_new_) { if (store_out_.is_open()) { // Write directly to store file store_out_.write(buf_.data() + pos_, static_cast<std::streamsize>(n)); if (!store_out_.good()) { Loading @@ -1301,7 +1303,7 @@ private: buf_.data() + pos_ + n); } } // Skip data for existing media (just advance pos_) // Advance pos_ pos_ += n; data_written_ += n; } Loading @@ -1324,12 +1326,13 @@ private: db_.media_[mid] = std::move(*pending_media_); db_.albums_[aid].media_ids.push_back(mid); pending_media_.reset(); } // Cluster: replicate binary data as separate "media:<id>" key if (stream_to_cluster_ && !media_data_buf_.empty()) { auto data = std::make_shared<std::vector<uint8_t>>( std::move(media_data_buf_)); std::string key = "media:" + mid; std::string key = "media:" + cur_media_id_; { std::lock_guard<std::mutex> dlk(deferred_mutex_); pending_ops_.push_back([this, key, data]() { Loading @@ -1338,7 +1341,7 @@ private: } } media_data_buf_.clear(); } ++mi_; if (mi_ < num_media_) { phase_ = Phase::MEDIA_HEADER; Loading