Fix a chicken & egg problem with queueingStructure.json monitor

ISSUE #209

Using the inode watcher on a file that doesn't exist fails, and
was previously failing silently! This would result in queue
mappings not updating when LibreQoS.py was executed - even though
the queueingStructure.json file became available.

* Replace "anyhow" with specific errors.
* Track and log each step of the file monitor process for
  queueingStructure.json
* If the watcher cannot start because the file doesn't exist,
  the watcher loop sleeps for 30 seconds at a time (to keep
  load very low) and checks if the file exists yet. If it does,
  it loads it and then commences watching.
This commit is contained in:
Herbert Wolverson
2023-01-31 15:49:29 +00:00
parent 6f00386d61
commit c911d8c190
3 changed files with 64 additions and 9 deletions

1
src/rust/Cargo.lock generated
View File

@@ -1384,6 +1384,7 @@ dependencies = [
"rayon",
"serde",
"serde_json",
"thiserror",
"tokio",
]

View File

@@ -5,6 +5,7 @@ edition = "2021"
[dependencies]
anyhow = "1"
thiserror = "1"
serde = "1"
serde_json = "1"
lqos_bus = { path = "../lqos_bus" }

View File

@@ -1,10 +1,13 @@
use std::time::Duration;
use crate::queue_structure::{
queue_network::QueueNetwork, queue_node::QueueNode, read_queueing_structure,
};
use anyhow::Result;
use lazy_static::*;
use parking_lot::RwLock;
use thiserror::Error;
use tokio::task::spawn_blocking;
use log::{info, error};
lazy_static! {
/// Global storage of the shaped devices csv data.
@@ -48,16 +51,66 @@ pub async fn spawn_queue_structure_monitor() {
/// Fires up a Linux file system watcher than notifies
/// when `ShapedDevices.csv` changes, and triggers a reload.
fn watch_for_queueing_structure_changing() -> Result<()> {
fn watch_for_queueing_structure_changing() -> Result<(), QueueWatcherError> {
info!("Starting the queue structure monitor.");
use notify::{Config, RecursiveMode, Watcher};
let (tx, rx) = std::sync::mpsc::channel();
let mut watcher = notify::RecommendedWatcher::new(tx, Config::default())?;
// Obtain the path to watch
let watch_path = QueueNetwork::path();
if watch_path.is_err() {
error!("Could not create path for queuingStructure.json");
return Err(QueueWatcherError::CannotCreatePath);
}
let watch_path = watch_path.unwrap();
watcher.watch(&QueueNetwork::path()?, RecursiveMode::NonRecursive)?;
loop {
let _ = rx.recv();
log::info!("queuingStructure.csv changed");
QUEUE_STRUCTURE.write().update();
// File notify doesn't work for files that don't exist
// It's quite possible that a user is just starting, and will
// not have a queueingStructure.json yet - so we need to keep
// trying to obtain one.
if !watch_path.exists() {
info!("queueingStructure.json does not exist yet.");
loop {
std::thread::sleep(Duration::from_secs(30));
if watch_path.exists() {
info!("queueingStructure.json was just created. Sleeping 1 second and watching it.");
std::thread::sleep(Duration::from_secs(1));
QUEUE_STRUCTURE.write().update();
break;
}
}
}
// Build the monitor
let (tx, rx) = std::sync::mpsc::channel();
let watcher = notify::RecommendedWatcher::new(tx, Config::default());
if watcher.is_err() {
error!("Could not create file watcher for queueingStructure.json");
error!("{:?}", watcher);
return Err(QueueWatcherError::WatcherFail);
}
let mut watcher = watcher.unwrap();
// Start monitoring
let result = watcher.watch(&watch_path, RecursiveMode::NonRecursive);
if result.is_ok() {
info!("Watching queueingStructure.csv for changes.");
loop {
let _ = rx.recv();
log::info!("queuingStructure.csv changed");
QUEUE_STRUCTURE.write().update();
}
} else {
error!("Unable to start queueingStructure watcher.");
error!("{:?}", watcher);
Err(QueueWatcherError::WatcherFail)
}
}
#[derive(Error, Debug)]
pub enum QueueWatcherError {
#[error("Could not create the path buffer to find queuingStructure.json")]
CannotCreatePath,
#[error("Cannot watch queueingStructure.json")]
WatcherFail,
}