Skip to main content

Module reloader

Module reloader 

Source
Available on crate features config-reload and config only.
Expand description

Universal configuration reloader for DFE components.

ConfigReloader<T> provides three reload triggers, any combination of which can be enabled simultaneously:

  1. SIGHUP (Unix only) – standard daemon reload signal
  2. Periodic timer – reload every N seconds
  3. File polling – detect config file changes via mtime comparison

The reloader calls a user-supplied reload_fn to load config and a validate_fn to validate before applying. On success it updates the SharedConfig<T> which notifies all subscribers.

§Usage

use std::path::PathBuf;
use std::time::Duration;
use hyperi_rustlib::config::reloader::{ConfigReloader, ReloaderConfig};
use hyperi_rustlib::config::shared::SharedConfig;

#[derive(Clone, Debug, Default)]
struct AppConfig {
    pub workers: usize,
}

#[tokio::main]
async fn main() {
    let config = AppConfig { workers: 4 };
    let shared = SharedConfig::new(config);

    let reloader_config = ReloaderConfig {
        config_path: Some(PathBuf::from("config.yaml")),
        poll_interval: Duration::from_secs(5),
        periodic_interval: Duration::ZERO,  // disabled
        debounce: Duration::from_millis(500),
        enable_sighup: true,
    };

    let reloader = ConfigReloader::new(
        reloader_config,
        shared.clone(),
        || {
            // Your config loading logic here
            Ok(AppConfig { workers: 8 })
        },
        |cfg| {
            // Your validation logic here
            if cfg.workers == 0 {
                return Err("workers must be > 0".into());
            }
            Ok(())
        },
    );

    let _handle = reloader.start();
    // ... run your application ...
}

§Migration from Component-Specific Implementations

§From dfe-loader’s ConfigWatcher (file polling)

// Before:
let watcher = ConfigWatcher::new(WatcherConfig {
    config_path, poll_interval, debounce, enabled: true,
}, shared)?;
let _handle = watcher.start();

// After:
let reloader = ConfigReloader::new(
    ReloaderConfig {
        config_path: Some(config_path),
        poll_interval,
        debounce,
        enable_sighup: true,      // bonus: also reload on SIGHUP
        periodic_interval: Duration::ZERO,
    },
    shared,
    || Config::load(path),        // your reload function
    |c| c.validate(),             // your validate function
);
let _handle = reloader.start();

§From dfe-receiver’s config_reload_task (SIGHUP + periodic)

// Before (inline in main.rs):
tokio::spawn(config_reload_task(state, reload_secs));

// After:
let reloader = ConfigReloader::new(
    ReloaderConfig {
        periodic_interval: Duration::from_secs(reload_secs),
        enable_sighup: true,
        config_path: None,         // no file watching
        ..Default::default()
    },
    shared,
    || Config::load(path),
    |c| c.validate(),
);
let _handle = reloader.start();

§From dfe-archiver (not yet wired)

The archiver has SharedConfig and reload_config() ready but not connected. Use ConfigReloader to complete the integration:

let reloader = ConfigReloader::new(
    ReloaderConfig {
        config_path: config.config_path.as_ref().map(PathBuf::from),
        periodic_interval: Duration::from_secs(config.config_reload_secs),
        enable_sighup: true,
        ..Default::default()
    },
    shared,
    || load_config(config_path),
    |c| validate_config(c),
);
let _handle = reloader.start();

Structs§

ConfigReloader
ReloaderConfig
Configuration for the reloader.