feat(job-registry): wire dedub_gc
This commit is contained in:
@@ -1277,6 +1277,27 @@ impl AppServiceFactory {
|
||||
// 1. Core services (PgPool needed for DedupService index)
|
||||
let core = self.create_core_services(&pool, &maintenance_pool).await?;
|
||||
|
||||
// Register on-demand-only jobs whose owning service lives on
|
||||
// CoreServices. Dedup GC has NO periodic tick — trash cleanup's
|
||||
// sweep already runs GC as its tail step, so a periodic dedup
|
||||
// schedule would double the work. Registering with `interval =
|
||||
// None` keeps it admin-triggerable through the uniform scheduler
|
||||
// surface (`POST /api/admin/internal/trigger-job/dedup_gc`).
|
||||
if let Err(e) = core
|
||||
.job_registry
|
||||
.register(
|
||||
core.dedup_service.clone()
|
||||
as Arc<dyn crate::infrastructure::scheduler::JobHandler>,
|
||||
None, // on-demand only
|
||||
None, // no timeout
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::error!("Failed to register dedup_gc job with scheduler: {e}");
|
||||
} else {
|
||||
tracing::info!("Dedup GC registered with scheduler (on-demand only)");
|
||||
}
|
||||
|
||||
// 2. Repository services (requires PgPool for all metadata)
|
||||
let repos = self.create_repository_services(&core, &pool);
|
||||
|
||||
|
||||
@@ -3120,6 +3120,46 @@ impl DedupPort for DedupService {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── JobRegistry integration ────────────────────────────────────────────────
|
||||
|
||||
/// Registered name for the dedup GC job. Stable identifier used in
|
||||
/// log lines, `admin.background_runs.job_name` (when Part 2 lands),
|
||||
/// and admin URLs (`POST /api/admin/internal/trigger-job/dedup_gc`).
|
||||
pub const DEDUP_GC_JOB_NAME: &str = "dedup_gc";
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl crate::infrastructure::scheduler::JobHandler for DedupService {
|
||||
fn name(&self) -> &str {
|
||||
DEDUP_GC_JOB_NAME
|
||||
}
|
||||
|
||||
/// Runs one `garbage_collect` sweep — the same reclamation that
|
||||
/// `TrashCleanupService` invokes inline as its tail step, exposed
|
||||
/// through the scheduler so operators can trigger it uniformly via
|
||||
/// `POST /api/admin/internal/trigger-job/dedup_gc`.
|
||||
///
|
||||
/// Registered with `interval = None` (on-demand only): the periodic
|
||||
/// tick belongs to trash cleanup, whose sweep already runs GC as
|
||||
/// its final phase. Registering a redundant periodic tick here
|
||||
/// would double the reclamation work with no benefit; the admin
|
||||
/// trigger is what the registered entry buys us — uniform log lines,
|
||||
/// panic containment, exclusivity vs. any concurrent trigger.
|
||||
///
|
||||
/// `count` reports blobs reclaimed; `extra.bytes_reclaimed` reports
|
||||
/// the freed disk. GC returning `(0, 0)` is normal — it means trash
|
||||
/// cleanup already reaped everything.
|
||||
async fn run(&self) -> crate::infrastructure::scheduler::JobOutcome {
|
||||
use crate::infrastructure::scheduler::JobOutcome;
|
||||
match self.garbage_collect().await {
|
||||
Ok((items, bytes)) => JobOutcome::ok_with(
|
||||
items,
|
||||
serde_json::json!({ "bytes_reclaimed": bytes }),
|
||||
),
|
||||
Err(e) => JobOutcome::Err(format!("dedup GC failed: {e}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Tests ───────────────────────────────────────────────────────────────────
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user