fix(analysis): reclaim orphaned leases from analysis daemon startup too (0.87.7)

`reclaim_orphaned` previously ran only inside `spawn_crawler_daemon` at
startup. A deploy with the crawler disabled and analysis enabled
(analysis-only mode) never refunded orphaned `analyze_page` leases —
recovery deferred to the lazy lease-expiry path. Wire it into
`spawn_analysis_daemon` too. Safe under multi-replica + idempotent
against the crawler-side call.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-22 21:44:14 +02:00
parent b9dd75684e
commit 6444ddee29
4 changed files with 23 additions and 5 deletions

2
backend/Cargo.lock generated
View File

@@ -1517,7 +1517,7 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
[[package]]
name = "mangalord"
version = "0.87.6"
version = "0.87.7"
dependencies = [
"anyhow",
"argon2",

View File

@@ -1,6 +1,6 @@
[package]
name = "mangalord"
version = "0.87.6"
version = "0.87.7"
edition = "2021"
default-run = "mangalord"

View File

@@ -266,7 +266,8 @@ impl DaemonReloader for Supervisors {
Arc::clone(&self.storage),
&cfg,
Arc::clone(&self.analysis_events),
)?;
)
.await?;
*guard = Some(handle);
tracing::info!(model = %cfg.model, "analysis daemon (re)started from settings");
} else {
@@ -405,12 +406,29 @@ async fn load_effective_analysis(
/// Spawn the AI content-analysis worker daemon with the given config. Returns
/// its handle. Independent of the crawler daemon (works for uploads with the
/// crawler off). Uses a plain reqwest client — no cookie jar / proxy.
fn spawn_analysis_daemon(
async fn spawn_analysis_daemon(
db: PgPool,
storage: Arc<dyn Storage>,
cfg: &AnalysisConfig,
events: Arc<crate::analysis::events::AnalysisEvents>,
) -> anyhow::Result<crate::analysis::daemon::AnalysisDaemonHandle> {
// Reclaim jobs orphaned by a previous crash/kill. `crawler::jobs::reclaim_orphaned`
// is keyed only on `state='running' AND leased_until < now()`, so it
// covers any kind that uses the table — including this daemon's
// `analyze_page` jobs. Previously this ran only inside
// `spawn_crawler_daemon`, which meant a deploy with the crawler off
// (analysis-only) never refunded orphaned analysis leases until the
// lease expiry path lazily picked them up. Safe under multi-replica
// (only expired leases are touched) and idempotent (the crawler-side
// call happens-before this if both are running).
match crate::crawler::jobs::reclaim_orphaned(&db).await {
Ok(0) => {}
Ok(n) => tracing::info!(
reclaimed = n,
"analysis: reclaimed orphaned in-flight jobs at startup"
),
Err(e) => tracing::warn!(?e, "analysis: reclaim_orphaned at startup failed"),
}
let http = reqwest::Client::builder()
.timeout(cfg.request_timeout)
// Refuse to honour ambient HTTP_PROXY / HTTPS_PROXY container env.

View File

@@ -1,6 +1,6 @@
{
"name": "mangalord-frontend",
"version": "0.87.6",
"version": "0.87.7",
"private": true,
"type": "module",
"scripts": {