Skip to content

Commit caaee5a

Browse files
authored
Merge pull request #62 from Quantus-Network/hot_fix_infinite_loop
hot_fix: looping hot-reload flag config
2 parents 6d50e03 + 753ab13 commit caaee5a

1 file changed

Lines changed: 60 additions & 14 deletions

File tree

src/services/wallet_feature_flags_service.rs

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
use notify::{Config as NotifyConfig, RecommendedWatcher, RecursiveMode, Watcher};
1+
use notify::{event::ModifyKind, Config as NotifyConfig, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
22
use serde_json::Value;
33
use std::{
44
path::{Path, PathBuf},
55
sync::{Arc, RwLock},
6+
time::Duration,
67
};
7-
use tokio::{sync::mpsc, task::JoinHandle};
8+
use tokio::{sync::mpsc, task::JoinHandle, time};
89

910
#[derive(Debug, thiserror::Error)]
1011
pub enum WalletFeatureFlagsError {
@@ -28,6 +29,14 @@ pub struct WalletFeatureFlagsService {
2829
}
2930

3031
impl WalletFeatureFlagsService {
32+
fn is_reload_event_kind(kind: &EventKind) -> bool {
33+
match kind {
34+
EventKind::Create(_) | EventKind::Modify(ModifyKind::Name(_)) => true,
35+
EventKind::Modify(modify_kind) => !matches!(modify_kind, ModifyKind::Metadata(_) | ModifyKind::Other),
36+
_ => false,
37+
}
38+
}
39+
3140
pub fn new(file_path: impl Into<PathBuf>) -> Result<Self, WalletFeatureFlagsError> {
3241
let file_path = file_path.into();
3342

@@ -50,23 +59,63 @@ impl WalletFeatureFlagsService {
5059
watcher.watch(parent_dir, RecursiveMode::NonRecursive)?;
5160

5261
let wallet_feature_flags_clone = wallet_feature_flags.clone();
62+
let watched_file_name = file_path.file_name().map(|n| n.to_os_string());
63+
let debounce_duration = Duration::from_millis(250);
5364

5465
let watch_task = tokio::spawn(async move {
55-
while let Some(result) = rx.recv().await {
56-
match result {
57-
Ok(event) => {
58-
// This ensures Create, Rename, and Modify events triggered by atomic saves are caught.
59-
let should_reload = event.paths.iter().any(|p| p.file_name() == file_path.file_name());
60-
61-
if !should_reload {
62-
continue;
66+
// Atomic file saves commonly emit bursts of events (Create/Rename/Modify).
67+
// We debounce these bursts to avoid reloading (and logging) repeatedly.
68+
let mut reload_pending = false;
69+
let reload_sleep = time::sleep(debounce_duration);
70+
tokio::pin!(reload_sleep);
71+
72+
loop {
73+
tokio::select! {
74+
biased;
75+
maybe_result = rx.recv() => {
76+
let Some(result) = maybe_result else {
77+
break;
78+
};
79+
80+
match result {
81+
Ok(event) => {
82+
if !Self::is_reload_event_kind(&event.kind) {
83+
continue;
84+
}
85+
86+
let should_reload = watched_file_name
87+
.as_deref()
88+
.map(|name| event.paths.iter().any(|p| p.file_name() == Some(name)))
89+
.unwrap_or(false);
90+
91+
if !should_reload {
92+
continue;
93+
}
94+
95+
reload_pending = true;
96+
reload_sleep.as_mut().reset(time::Instant::now() + debounce_duration);
97+
}
98+
Err(err) => {
99+
tracing::error!("Wallet feature flags watcher error: {}", err);
100+
}
63101
}
102+
}
103+
_ = &mut reload_sleep, if reload_pending => {
104+
reload_pending = false;
64105

65106
match Self::read_flags_from_file_async(&file_path).await {
66107
Ok(updated_flags) => {
67108
if let Ok(mut write_guard) = wallet_feature_flags_clone.write() {
109+
if *write_guard == updated_flags {
110+
// Avoid noisy log spam when events are emitted without content changes.
111+
continue;
112+
}
113+
68114
*write_guard = updated_flags;
69-
tracing::info!("Wallet feature flags reloaded from {}", file_path.display());
115+
tracing::info!(
116+
"Wallet feature flags reloaded from {}",
117+
file_path.display()
118+
);
70119
}
71120
}
72121
Err(err) => {
@@ -78,9 +127,6 @@ impl WalletFeatureFlagsService {
78127
}
79128
}
80129
}
81-
Err(err) => {
82-
tracing::error!("Wallet feature flags watcher error: {}", err);
83-
}
84130
}
85131
}
86132
});

0 commit comments

Comments
 (0)