From ce56e31de2800f5d829f4de61a5913df1ee37adc Mon Sep 17 00:00:00 2001 From: vegard Date: Wed, 18 Mar 2026 05:57:10 +0000 Subject: [PATCH] =?UTF-8?q?Fullf=C3=B8rer=20oppgave=2017.6:=20Periodisk=20?= =?UTF-8?q?CAS=20tmp-opprydding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Legger til cleanup_tmp() i CasStore som sletter orphaned .tmp-filer eldre enn 1 time. Disse oppstår når en skriveprosess krasjer midt i en atomisk CAS-skriveoperasjon (skriv til tmp, rename til endelig path). Ny bakgrunnsloop start_tmp_cleanup_loop() kjører hver time og fjerner foreldede temp-filer. Følger samme mønster som pruning- og disk-monitor-loopene. Co-Authored-By: Claude Opus 4.6 (1M context) --- maskinrommet/src/cas.rs | 77 ++++++++++++++++++++++++++++++++++++++++ maskinrommet/src/main.rs | 3 ++ tasks.md | 3 +- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/maskinrommet/src/cas.rs b/maskinrommet/src/cas.rs index 2ab23fe..59e3901 100644 --- a/maskinrommet/src/cas.rs +++ b/maskinrommet/src/cas.rs @@ -167,6 +167,83 @@ impl CasStore { fn tmp_path(&self, hash: &str) -> PathBuf { self.root.join("tmp").join(format!("{hash}.tmp")) } + + /// Slett temp-filer eldre enn `max_age` fra CAS tmp-katalogen. + /// Returnerer (antall slettet, bytes frigitt). + pub async fn cleanup_tmp(&self, max_age: std::time::Duration) -> std::io::Result<(usize, u64)> { + let tmp_dir = self.root.join("tmp"); + if !tmp_dir.exists() { + return Ok((0, 0)); + } + + let mut deleted = 0usize; + let mut freed = 0u64; + let now = std::time::SystemTime::now(); + + let mut entries = tokio::fs::read_dir(&tmp_dir).await?; + while let Some(entry) = entries.next_entry().await? { + let meta = match entry.metadata().await { + Ok(m) => m, + Err(_) => continue, + }; + if !meta.is_file() { + continue; + } + let modified = match meta.modified() { + Ok(t) => t, + Err(_) => continue, + }; + let age = now.duration_since(modified).unwrap_or_default(); + if age > max_age { + let size = meta.len(); + if let Ok(()) = tokio::fs::remove_file(entry.path()).await { + deleted += 1; + freed += size; + tracing::debug!( + file = %entry.file_name().to_string_lossy(), + age_secs = age.as_secs(), + size, + "Slettet gammel temp-fil" + ); + } + } + } + + Ok((deleted, freed)) + } +} + +/// Start periodisk opprydding av CAS tmp-filer. +/// Kjører hver time, sletter filer eldre enn 1 time. +pub fn start_tmp_cleanup_loop(cas: CasStore) { + tokio::spawn(async move { + // Vent 45 sekunder etter oppstart + tokio::time::sleep(std::time::Duration::from_secs(45)).await; + tracing::info!("CAS tmp-opprydding startet (intervall: 1t, TTL: 1t)"); + + let max_age = std::time::Duration::from_secs(3600); // 1 time + let interval = std::time::Duration::from_secs(3600); // 1 time + + loop { + match cas.cleanup_tmp(max_age).await { + Ok((deleted, freed)) => { + if deleted > 0 { + tracing::info!( + deleted, + freed_kb = freed / 1024, + "CAS tmp-opprydding: {} filer slettet, {} KB frigitt", + deleted, + freed / 1024, + ); + } + } + Err(e) => { + tracing::warn!(error = %e, "CAS tmp-opprydding feilet"); + } + } + tokio::time::sleep(interval).await; + } + }); } #[cfg(test)] diff --git a/maskinrommet/src/main.rs b/maskinrommet/src/main.rs index db11d80..05e1d7d 100644 --- a/maskinrommet/src/main.rs +++ b/maskinrommet/src/main.rs @@ -171,6 +171,9 @@ async fn main() { // Start nattlig bandwidth-parsing (oppgave 15.7) bandwidth::start_bandwidth_parser(db.clone()); + // Start periodisk CAS tmp-opprydding (oppgave 17.6) + cas::start_tmp_cleanup_loop(cas.clone()); + let index_cache = publishing::new_index_cache(); let dynamic_page_cache = publishing::new_dynamic_page_cache(); let state = AppState { db, jwks, stdb, cas, index_cache, dynamic_page_cache, maintenance, priority_rules }; diff --git a/tasks.md b/tasks.md index 324f44d..4a933ab 100644 --- a/tasks.md +++ b/tasks.md @@ -194,8 +194,7 @@ Ref: Kodegjennomgang av `b4c4bb8` (Lydstudio: lydredigering via FFmpeg). - [x] 17.3 Fade/silence-logikk: fiks negativ fade-out start (clamp til 0), og adaptiv silence-margin (margin skal ikke overstige halve regionens varighet). Gi feilmelding ved ugyldige fade-varigheter. - [x] 17.4 Frontend input-begrensninger: legg til `min`/`max` på alle tallfelter i OperationPanel (silenceThreshold, fadeMs, normTarget, compRatio). Hindre ugyldig input. - [x] 17.5 Job-polling opprydding: rydd opp interval/timeout ved navigering bort fra studio-siden. Vis feilmelding etter N mislykkede polling-forsøk. Wrap metadata JSON.parse i try/catch. -- [~] 17.6 Temp-fil opprydding: legg til periodisk jobb i maskinrommet som sletter gamle temp-filer i CAS tmp-katalog. Bruk `/tmp` eller sett TTL. - > Påbegynt: 2026-03-18T05:54 +- [x] 17.6 Temp-fil opprydding: legg til periodisk jobb i maskinrommet som sletter gamle temp-filer i CAS tmp-katalog. Bruk `/tmp` eller sett TTL. - [ ] 17.7 FFmpeg feilmeldinger til bruker: propager stderr fra FFmpeg-feil til frontend via strukturert feilrespons. Vis i RenderDialog. ## Fase 18: AI-verktøy (arbeidsflate)