From dead2d9cd19277c6772050c7b84a7dde593858b8 Mon Sep 17 00:00:00 2001 From: cryptonautt Date: Sat, 28 Mar 2026 12:59:48 +0100 Subject: [PATCH] Fix: implement background task startup idempotency --- services/api/src/blockchain.rs | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/services/api/src/blockchain.rs b/services/api/src/blockchain.rs index b760d2d..6ba4bc1 100644 --- a/services/api/src/blockchain.rs +++ b/services/api/src/blockchain.rs @@ -1,6 +1,8 @@ use std::{ - collections::HashSet, - sync::Arc, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, time::{Duration, SystemTime, UNIX_EPOCH}, }; @@ -36,6 +38,7 @@ pub struct BlockchainClient { #[derive(Default)] struct MonitoringState { watched_txs: RwLock>, + tasks_started: AtomicBool, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -720,6 +723,11 @@ impl BlockchainClient { } pub fn start_background_tasks(self: Arc) { + if self.monitor.tasks_started.swap(true, Ordering::SeqCst) { + tracing::warn!("background tasks already started; skipping duplicate invocation"); + return; + } + let sync_client = self.clone(); tokio::spawn(async move { sync_client.run_sync_worker().await; @@ -886,4 +894,23 @@ mod tests { assert_eq!(*cache.purged_count.lock().await, 0); assert_eq!(*metrics.invalidation_count.lock().unwrap(), 0); } + + #[tokio::test] + async fn test_start_background_tasks_idempotency() { + let config = Config::from_env(); + let metrics = Metrics::new().unwrap(); + let cache = RedisCache::new("redis://127.0.0.1").await.unwrap(); + let client = Arc::new(BlockchainClient::new(&config, cache, metrics).unwrap()); + + // Initial state: not started + assert!(!client.monitor.tasks_started.load(Ordering::SeqCst)); + + // First call + client.clone().start_background_tasks(); + assert!(client.monitor.tasks_started.load(Ordering::SeqCst)); + + // Second call should not panic or change state (stays true) + client.clone().start_background_tasks(); + assert!(client.monitor.tasks_started.load(Ordering::SeqCst)); + } }