Skip to main content

Module jobs

Module jobs 

Source
Expand description

Background jobs: one persisted row per long-running administrative operation, run on an in-process thread with progress persisted to the database. The shared mechanism behind corpus import/extend, service activation, runs, and dataset export. See docs/archive/JOB_MODEL.md.

Structs§

Job
A persisted background job.
JobProgress
A handle passed to a job body; each call persists progress on the job row.

Constants§

ANALYZE_KIND
The job kind for a planner-statistics refresh.
POPULATE_REPORT_KIND
The job kind for a single (corpus, service, severity) report-cache populate.
REFRESH_REPORTS_KIND
The job kind for a report_summary rollup refresh.
REINDEX_KIND
The job kind for an online index rebuild.

Functions§

count_recent_with_status
Counts jobs in terminal status created within the last hours — a current-state observability signal for “are jobs failing / stalling lately?”. A rolling window so the gauge auto-resolves (unlike an ever-growing total-failures counter, which would alert forever after one failure). Skew-free (windowed against the DB clock). Best-effort: 0 on error.
db_now
The database’s current wall clock as a tz-naive timestamp in the session timezone — i.e. the same clock created_at/updated_at are written in (Postgres now() stored into a timestamp column). Differencing a job’s updated_at against this is therefore skew-free, unlike comparing against the app process’s chrono::Utc::now() (which differs by the session offset when the DB is not on UTC). Used to compute a running job’s heartbeat age (W-4 stall observability). Best-effort: returns None if the probe fails (a broken connection), so callers degrade to “no age” rather than reporting a bogus one.
find_job
Finds a job by its external uuid handle.
interrupt_orphans
Marks any non-terminal jobs as interrupted; call once on frontend startup so jobs that died with a previous process are not left looking live.
list_recent
Lists recent jobs, most-recent-first, capped at limit. With active_only, returns just the pending (non-terminal: queued/running) jobs — the fleet-wide observability check for any background-task capability. Best-effort: an error yields an empty list rather than propagating.
reap_stale
Marks any non-terminal job whose progress heartbeat has been silent for longer than config().jobs.stale_timeout_seconds as interrupted — the runtime complement to interrupt_orphans (which only runs at startup). It closes the W-4 zombie: a job whose body hangs while a long-lived frontend keeps running would otherwise sit running forever, leaking a thread and lying to every pending-check + the report-refresh debounce. A job that keeps step-ing stays live (fresh updated_at); only a silent one is reaped. Self-correcting: if a merely-slow job is reaped and later finishes, its finish() overwrites the status, so a generous timeout costs at most a transient interrupted display. Skew-free (differences against the DB clock, like db_now). Returns the count reaped; best-effort.
report_populate_active
Whether a populate job for exactly this (corpus, service, severity) slice is already queued or running — the request path’s pre-check, so a burst of viewers of a still-computing report doesn’t each re-attempt the (bounded) inline aggregation while the background job is already on it.
spawn_analyze
Spawns a background job that refreshes the query planner’s statistics with ANALYZE over the high-churn tables. After a bulk import or a large rerun churns tasks.status, stale statistics can make the planner mis-estimate and skip the right index (e.g. the TODO leasing index, todo_index), so an operator can refresh them on demand instead of waiting for autovacuum’s next pass (DB ongoing-maintenance; docs/DB_TUNING.md). ANALYZE is online (a brief lock per table, sampling only) and runs off the request path, reporting per-table progress. Debounced: an analyze already queued/running is reused.
spawn_job
Spawns a background job: inserts a queued row, returns its uuid, and runs body on a thread. The body reports progress via JobProgress and returns a terminal result or an error message.
spawn_reindex
Spawns a background job that rebuilds the high-churn tables’ indexes online with REINDEX (CONCURRENTLY) — no exclusive lock, so reads/writes continue (DB ongoing-maintenance; docs/DB_TUNING.md). Runs off the request path (rebuilds are minutes-to-hours at scale) and reports per-table progress. Debounced: a reindex already queued/running is reused.
spawn_report_populate
Spawns a background job that (re)computes one (corpus, service, severity) report slice (rollup::populate_scope) off the request path — the heavy aggregation behind a cold report view (minutes for the full-arXiv info slice) runs here instead of pinning a frontend connection and blocking the viewer, who is shown a “report computing” page that refreshes when the slice is ready. Debounced per scope: an already-active populate for the same slice is reused (its uuid returned) rather than spawning a duplicate. Poll GET /api/jobs/<uuid> for status/health.
spawn_report_refresh
Spawns a background job for the manual “Force refresh” action. The global report_summary matview was retired in favour of a per-(corpus, service, severity) cache (report_grain_cache), so this no longer does a multi-minute rebuild: it invalidates the whole cache (a cheap DELETE, never a scan), and each report slice then repopulates lazily, per scope, on its next view. Run off the request path so the caller returns immediately.