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

deb

parent 2c9f51a2
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -11,6 +11,12 @@ mediadb (20260422+59) unstable; urgency=low
  * Fix cluster status bar: use health_loop assessment (isDegraded/
    isCritical) instead of raw pclient snapshot which could show
    DEGRADED while the health loop had already suppressed it.
  * Add detailed import progress: ImportSession::progress() reports
    phase (starting/parsing/importing/replicating/finalizing), bytes
    fed, media count, and store count.  Exposed via /api/import/status
    and /api/cluster/status import_progress object.
  * cluster.html: show import detail card with phase, MB transferred,
    media count and store progress during active imports.

 -- Jan Koester <jan.koester@tuxist.de>  Wed, 22 Apr 2026 00:00:00 +0200

+12 −0
Original line number Diff line number Diff line
@@ -151,6 +151,18 @@ async function loadClusterStatus() {
    health.nodes_online === health.nodes_total ? '' : 'warn');
  addSummaryCard(summary, 'Import', health.import_running ? 'In Progress' : 'Idle',
    health.import_running ? 'warn' : '');
  if (health.import_running && health.import_progress) {
    const ip = health.import_progress;
    const phaseName = {starting:'Starting',parsing:'Parsing',importing:'Importing',replicating:'Replicating',finalizing:'Finalizing',done:'Done',failed:'Failed'}[ip.phase] || ip.phase;
    let detail = phaseName;
    if (ip.bytes_fed > 0) {
      const mb = (ip.bytes_fed / (1024*1024)).toFixed(1);
      detail += '' + mb + ' MB';
    }
    if (ip.media_done > 0) detail += ', ' + ip.media_done + ' media';
    if (ip.stores_total > 0) detail += ', store ' + ip.stores_done + '/' + ip.stores_total;
    addSummaryCard(summary, 'Import Detail', detail, '');
  }

  if (stores.stores) {
    const missing = stores.stores.filter(s => s.replicated_count < s.total_nodes).length;
+27 −0
Original line number Diff line number Diff line
@@ -519,6 +519,8 @@ std::unique_ptr<ImportSession> App::begin_import() {
        import_running_.store(false);
    } else {
        std::cerr << "[import] begin_import: session created OK\n";
        std::lock_guard<std::mutex> lk(import_mutex_);
        active_import_session_ = session.get();
    }
    return session;
}
@@ -527,6 +529,7 @@ void App::finish_import(bool ok, const std::string& error) {
    std::lock_guard<std::mutex> lk(import_mutex_);
    import_ok_.store(ok);
    if (!ok && !error.empty()) import_error_ = error;
    active_import_session_ = nullptr;
    import_running_.store(false);
    if (g_Cluster) g_Cluster->setImportRunning(false);
}
@@ -658,6 +661,16 @@ HttpResponse App::handle_import_status(const HttpRequest& req) {
    json_object* j = json_object_new_object();
    if (import_running_.load()) {
        json_object_object_add(j, "status", json_object_new_string("running"));
        std::lock_guard<std::mutex> lk(import_mutex_);
        if (active_import_session_) {
            auto p = active_import_session_->progress();
            json_object_object_add(j, "phase", json_object_new_string(p.phase.c_str()));
            json_object_object_add(j, "bytes_fed", json_object_new_int64(static_cast<int64_t>(p.bytes_fed)));
            json_object_object_add(j, "media_done", json_object_new_int(static_cast<int>(p.media_done)));
            json_object_object_add(j, "media_total", json_object_new_int(static_cast<int>(p.media_total)));
            json_object_object_add(j, "stores_done", json_object_new_int(static_cast<int>(p.stores_done)));
            json_object_object_add(j, "stores_total", json_object_new_int(static_cast<int>(p.stores_total)));
        }
    } else if (import_ok_.load()) {
        json_object_object_add(j, "status", json_object_new_string("done"));
        json_object_object_add(j, "message", json_object_new_string("import successful"));
@@ -700,6 +713,20 @@ HttpResponse App::handle_cluster_status(const HttpRequest& req) {
    // Import status — always included regardless of cluster state
    json_object_object_add(j, "import_running",
        json_object_new_boolean(import_running_.load()));
    if (import_running_.load()) {
        std::lock_guard<std::mutex> lk(import_mutex_);
        if (active_import_session_) {
            auto p = active_import_session_->progress();
            json_object* ji = json_object_new_object();
            json_object_object_add(ji, "phase", json_object_new_string(p.phase.c_str()));
            json_object_object_add(ji, "bytes_fed", json_object_new_int64(static_cast<int64_t>(p.bytes_fed)));
            json_object_object_add(ji, "media_done", json_object_new_int(static_cast<int>(p.media_done)));
            json_object_object_add(ji, "media_total", json_object_new_int(static_cast<int>(p.media_total)));
            json_object_object_add(ji, "stores_done", json_object_new_int(static_cast<int>(p.stores_done)));
            json_object_object_add(ji, "stores_total", json_object_new_int(static_cast<int>(p.stores_total)));
            json_object_object_add(j, "import_progress", ji);
        }
    }

    if (!g_Cluster || !g_Cluster->isRunning()) {
        json_object_object_add(j, "enabled", json_object_new_boolean(false));
+3 −0
Original line number Diff line number Diff line
@@ -147,6 +147,9 @@ private:
    std::atomic<bool> import_ok_{false};
    std::string import_error_;
    std::thread import_thread_;
    // Observer pointer to active session (owned by server ImportState).
    // Only valid while import_running_ is true, guarded by import_mutex_.
    const ImportSession* active_import_session_ = nullptr;
};

} // namespace mediadb
+36 −0
Original line number Diff line number Diff line
@@ -1033,6 +1033,42 @@ public:
        return error_msg_;
    }

    Progress progress() const override {
        Progress p;
        p.bytes_fed = total_fed_;
        p.stores_total = num_stores_;
        p.stores_done = touched_stores_.size();
        p.media_total = num_media_;  // per-store count (resets per store)
        p.media_done = total_media_;

        if (done_.load(std::memory_order_acquire)) {
            p.phase = ok_.load(std::memory_order_acquire) ? "done" : "failed";
        } else if (parsing_done_.load(std::memory_order_acquire)) {
            p.phase = "replicating";
        } else {
            switch (phase_) {
            case Phase::MAGIC:
            case Phase::NUM_STORES:
                p.phase = "starting";
                break;
            case Phase::STORE_HEADER:
            case Phase::NUM_ALBUMS:
            case Phase::ALBUM_HEADER:
            case Phase::NUM_MEDIA:
                p.phase = "parsing";
                break;
            case Phase::MEDIA_HEADER:
            case Phase::MEDIA_DATA:
                p.phase = "importing";
                break;
            case Phase::DONE:
                p.phase = "finalizing";
                break;
            }
        }
        return p;
    }

private:
    // ---- read helpers (return false if not enough data) ----

Loading