From 746f3b31091f13700320e7b63cffabb388f3e5d8 Mon Sep 17 00:00:00 2001 From: Arnaud_Cayrol Date: Sat, 14 Feb 2026 16:28:33 +0100 Subject: [PATCH] Fix the processing sucesfull counter not being atomic --- .../src/lib/components/ProgressDisplay.svelte | 4 ++-- src/job/processing.rs | 1 + src/web/handlers/processing.rs | 3 +++ src/web/state.rs | 21 +++++++++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/components/ProgressDisplay.svelte b/frontend/src/lib/components/ProgressDisplay.svelte index 4312ebf..172d23b 100644 --- a/frontend/src/lib/components/ProgressDisplay.svelte +++ b/frontend/src/lib/components/ProgressDisplay.svelte @@ -84,8 +84,8 @@ .reduce((sum, [_, val]) => sum + (typeof val === 'number' ? val : 0), 0) ); - // Calculate kept (successful) count: completed - skipped - let keptCount = $derived(Math.max(0, displayCompleted - skipTotal)); + // Kept count from backend (tracked atomically alongside skip stats) + let keptCount = $derived(displaySkipStats.kept || 0); // Dynamically build skip reasons from whatever the backend sends // Filter to only show non-zero counts, exclude the 'total' field diff --git a/src/job/processing.rs b/src/job/processing.rs index 05743f9..77ab7c9 100644 --- a/src/job/processing.rs +++ b/src/job/processing.rs @@ -206,6 +206,7 @@ pub async fn process_single_asset( }; } + skip_stats.increment_kept(); AssetProcessResult::Success { asset_id } } PipelineResult::Skipped { diff --git a/src/web/handlers/processing.rs b/src/web/handlers/processing.rs index 04638a0..c3f02f7 100644 --- a/src/web/handlers/processing.rs +++ b/src/web/handlers/processing.rs @@ -18,6 +18,8 @@ pub struct SkipStatsResponse { pub counts: HashMap, /// Total number of skipped images. pub total: u32, + /// Number of images kept (passed all filters). + pub kept: u32, } impl From<&SkipStats> for SkipStatsResponse { @@ -25,6 +27,7 @@ impl From<&SkipStats> for SkipStatsResponse { Self { counts: stats.counts().clone(), total: stats.total(), + kept: stats.kept(), } } } diff --git a/src/web/state.rs b/src/web/state.rs index 4beab83..75683ca 100644 --- a/src/web/state.rs +++ b/src/web/state.rs @@ -56,6 +56,8 @@ impl std::fmt::Display for JobStatus { pub struct SkipStats { /// Map of skip reason ID to count. counts: HashMap, + /// Number of images kept (passed all filters). + kept: u32, } impl SkipStats { @@ -79,6 +81,16 @@ impl SkipStats { &self.counts } + /// Number of images kept (passed all filters). + pub fn kept(&self) -> u32 { + self.kept + } + + /// Set the kept count. + pub fn set_kept(&mut self, count: u32) { + self.kept = count; + } + /// Total number of skipped images. pub fn total(&self) -> u32 { self.counts.values().sum() @@ -94,6 +106,8 @@ pub struct AtomicSkipStats { /// Map of skip reason to atomic counter. /// Uses std::sync::RwLock for synchronous access (required for HashMap mutations). counts: StdRwLock>>, + /// Number of images kept (passed all filters). + kept: AtomicU32, } impl AtomicSkipStats { @@ -102,6 +116,11 @@ impl AtomicSkipStats { Self::default() } + /// Increment the kept counter. Returns the new count. + pub fn increment_kept(&self) -> u32 { + self.kept.fetch_add(1, Ordering::SeqCst) + 1 + } + /// Increment a counter for the given reason string. /// /// If the reason doesn't exist yet, it's created with an initial count of 1. @@ -136,6 +155,7 @@ impl AtomicSkipStats { for (reason, counter) in counts.iter() { stats.set(reason.clone(), counter.load(Ordering::SeqCst)); } + stats.set_kept(self.kept.load(Ordering::SeqCst)); stats } @@ -143,6 +163,7 @@ impl AtomicSkipStats { pub fn reset(&self) { let mut counts = self.counts.write().unwrap(); counts.clear(); + self.kept.store(0, Ordering::SeqCst); } }