mirror of https://github.com/telemt/telemt.git
Compare commits
No commits in common. "1544e3fcff7f92229514af651acb7e5848cbc29c" and "30ba41eb47aa0e845a2771f6a86ac75eb6ebe661" have entirely different histories.
1544e3fcff
...
30ba41eb47
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "telemt"
|
name = "telemt"
|
||||||
version = "3.3.22"
|
version = "3.3.21"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ use super::load::{LoadedConfig, ProxyConfig};
|
||||||
|
|
||||||
const HOT_RELOAD_STABLE_SNAPSHOTS: u8 = 2;
|
const HOT_RELOAD_STABLE_SNAPSHOTS: u8 = 2;
|
||||||
const HOT_RELOAD_DEBOUNCE: Duration = Duration::from_millis(50);
|
const HOT_RELOAD_DEBOUNCE: Duration = Duration::from_millis(50);
|
||||||
const HOT_RELOAD_STABLE_RECHECK: Duration = Duration::from_millis(75);
|
|
||||||
|
|
||||||
// ── Hot fields ────────────────────────────────────────────────────────────────
|
// ── Hot fields ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -380,14 +379,6 @@ impl ReloadState {
|
||||||
self.applied_snapshot_hash = Some(hash);
|
self.applied_snapshot_hash = Some(hash);
|
||||||
self.reset_candidate();
|
self.reset_candidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pending_candidate(&self) -> Option<(u64, u8)> {
|
|
||||||
let hash = self.candidate_snapshot_hash?;
|
|
||||||
if self.candidate_hits < HOT_RELOAD_STABLE_SNAPSHOTS {
|
|
||||||
return Some((hash, self.candidate_hits));
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalize_watch_path(path: &Path) -> PathBuf {
|
fn normalize_watch_path(path: &Path) -> PathBuf {
|
||||||
|
|
@ -1262,73 +1253,6 @@ fn reload_config(
|
||||||
Some(next_manifest)
|
Some(next_manifest)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn reload_with_internal_stable_rechecks(
|
|
||||||
config_path: &PathBuf,
|
|
||||||
config_tx: &watch::Sender<Arc<ProxyConfig>>,
|
|
||||||
log_tx: &watch::Sender<LogLevel>,
|
|
||||||
detected_ip_v4: Option<IpAddr>,
|
|
||||||
detected_ip_v6: Option<IpAddr>,
|
|
||||||
reload_state: &mut ReloadState,
|
|
||||||
) -> Option<WatchManifest> {
|
|
||||||
let mut next_manifest = reload_config(
|
|
||||||
config_path,
|
|
||||||
config_tx,
|
|
||||||
log_tx,
|
|
||||||
detected_ip_v4,
|
|
||||||
detected_ip_v6,
|
|
||||||
reload_state,
|
|
||||||
);
|
|
||||||
let mut rechecks_left = HOT_RELOAD_STABLE_SNAPSHOTS.saturating_sub(1);
|
|
||||||
|
|
||||||
while rechecks_left > 0 {
|
|
||||||
let Some((snapshot_hash, candidate_hits)) = reload_state.pending_candidate() else {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
|
|
||||||
info!(
|
|
||||||
snapshot_hash,
|
|
||||||
candidate_hits,
|
|
||||||
required_hits = HOT_RELOAD_STABLE_SNAPSHOTS,
|
|
||||||
rechecks_left,
|
|
||||||
recheck_delay_ms = HOT_RELOAD_STABLE_RECHECK.as_millis(),
|
|
||||||
"config reload: scheduling internal stable recheck"
|
|
||||||
);
|
|
||||||
tokio::time::sleep(HOT_RELOAD_STABLE_RECHECK).await;
|
|
||||||
|
|
||||||
let recheck_manifest = reload_config(
|
|
||||||
config_path,
|
|
||||||
config_tx,
|
|
||||||
log_tx,
|
|
||||||
detected_ip_v4,
|
|
||||||
detected_ip_v6,
|
|
||||||
reload_state,
|
|
||||||
);
|
|
||||||
if recheck_manifest.is_some() {
|
|
||||||
next_manifest = recheck_manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
if reload_state.is_applied(snapshot_hash) {
|
|
||||||
info!(
|
|
||||||
snapshot_hash,
|
|
||||||
"config reload: applied after internal stable recheck"
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if reload_state.pending_candidate().is_none() {
|
|
||||||
info!(
|
|
||||||
snapshot_hash,
|
|
||||||
"config reload: internal stable recheck aborted"
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
rechecks_left = rechecks_left.saturating_sub(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
next_manifest
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Public API ────────────────────────────────────────────────────────────────
|
// ── Public API ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Spawn the hot-reload watcher task.
|
/// Spawn the hot-reload watcher task.
|
||||||
|
|
@ -1452,16 +1376,14 @@ pub fn spawn_config_watcher(
|
||||||
tokio::time::sleep(HOT_RELOAD_DEBOUNCE).await;
|
tokio::time::sleep(HOT_RELOAD_DEBOUNCE).await;
|
||||||
while notify_rx.try_recv().is_ok() {}
|
while notify_rx.try_recv().is_ok() {}
|
||||||
|
|
||||||
if let Some(next_manifest) = reload_with_internal_stable_rechecks(
|
if let Some(next_manifest) = reload_config(
|
||||||
&config_path,
|
&config_path,
|
||||||
&config_tx,
|
&config_tx,
|
||||||
&log_tx,
|
&log_tx,
|
||||||
detected_ip_v4,
|
detected_ip_v4,
|
||||||
detected_ip_v6,
|
detected_ip_v6,
|
||||||
&mut reload_state,
|
&mut reload_state,
|
||||||
)
|
) {
|
||||||
.await
|
|
||||||
{
|
|
||||||
apply_watch_manifest(
|
apply_watch_manifest(
|
||||||
inotify_watcher.as_mut(),
|
inotify_watcher.as_mut(),
|
||||||
poll_watcher.as_mut(),
|
poll_watcher.as_mut(),
|
||||||
|
|
@ -1618,35 +1540,6 @@ mod tests {
|
||||||
let _ = std::fs::remove_file(path);
|
let _ = std::fs::remove_file(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn reload_cycle_applies_after_single_external_event() {
|
|
||||||
let initial_tag = "10101010101010101010101010101010";
|
|
||||||
let final_tag = "20202020202020202020202020202020";
|
|
||||||
let path = temp_config_path("telemt_hot_reload_single_event");
|
|
||||||
|
|
||||||
write_reload_config(&path, Some(initial_tag), None);
|
|
||||||
let initial_cfg = Arc::new(ProxyConfig::load(&path).unwrap());
|
|
||||||
let initial_hash = ProxyConfig::load_with_metadata(&path).unwrap().rendered_hash;
|
|
||||||
let (config_tx, _config_rx) = watch::channel(initial_cfg.clone());
|
|
||||||
let (log_tx, _log_rx) = watch::channel(initial_cfg.general.log_level.clone());
|
|
||||||
let mut reload_state = ReloadState::new(Some(initial_hash));
|
|
||||||
|
|
||||||
write_reload_config(&path, Some(final_tag), None);
|
|
||||||
reload_with_internal_stable_rechecks(
|
|
||||||
&path,
|
|
||||||
&config_tx,
|
|
||||||
&log_tx,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
&mut reload_state,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(config_tx.borrow().general.ad_tag.as_deref(), Some(final_tag));
|
|
||||||
let _ = std::fs::remove_file(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reload_keeps_hot_apply_when_non_hot_fields_change() {
|
fn reload_keeps_hot_apply_when_non_hot_fields_change() {
|
||||||
let initial_tag = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
let initial_tag = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue