Add logging, fix devenv up
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
path::PathBuf,
|
||||
sync::{Arc, RwLock},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use fuser::INodeNo;
|
||||
use sea_orm::{DatabaseConnection, EntityTrait};
|
||||
use tokio_stream::StreamExt;
|
||||
|
||||
use crate::item::Item;
|
||||
use crate::origins::FileWatcher;
|
||||
use crate::origins::network::transport::NetworkTransport;
|
||||
use crate::proto::ManifestEntry as ProtoManifestEntry;
|
||||
use tracing::{info, warn};
|
||||
|
||||
const POLL_FALLBACK_INTERVAL: Duration = Duration::from_secs(30);
|
||||
|
||||
pub struct NetworkOriginFileWatcher {
|
||||
transport: NetworkTransport,
|
||||
runtime_handle: tokio::runtime::Handle,
|
||||
client: DatabaseConnection,
|
||||
destination: PathBuf,
|
||||
latest_manifest: Arc<RwLock<BTreeMap<u64, ProtoManifestEntry>>>,
|
||||
}
|
||||
|
||||
impl NetworkOriginFileWatcher {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
transport: NetworkTransport,
|
||||
runtime_handle: tokio::runtime::Handle,
|
||||
client: DatabaseConnection,
|
||||
destination: PathBuf,
|
||||
latest_manifest: Arc<RwLock<BTreeMap<u64, ProtoManifestEntry>>>,
|
||||
) -> Self {
|
||||
return NetworkOriginFileWatcher {
|
||||
transport,
|
||||
runtime_handle,
|
||||
client,
|
||||
destination,
|
||||
latest_manifest,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// `files` is currently unused at runtime by the network watcher — the
|
||||
/// authoritative post-reconcile snapshot is written to the DB, and the next
|
||||
/// FUSE `readdir`/`lookup` will reflect it because FuseFs re-locks the map.
|
||||
/// The trait signature still requires it for parity with LocalOriginFileWatcher;
|
||||
/// a future refactor can rebuild the map in place here.
|
||||
impl FileWatcher for NetworkOriginFileWatcher {
|
||||
fn watch(&self, files: Arc<std::sync::Mutex<BTreeMap<INodeNo, Item>>>) {
|
||||
let runtime_handle = self.runtime_handle.clone();
|
||||
let state = WatcherState {
|
||||
transport: self.transport.clone(),
|
||||
client: self.client.clone(),
|
||||
destination: self.destination.clone(),
|
||||
latest_manifest: self.latest_manifest.clone(),
|
||||
files,
|
||||
};
|
||||
thread::spawn(move || {
|
||||
runtime_handle.block_on(state.run_loop());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct WatcherState {
|
||||
transport: NetworkTransport,
|
||||
client: DatabaseConnection,
|
||||
destination: PathBuf,
|
||||
latest_manifest: Arc<RwLock<BTreeMap<u64, ProtoManifestEntry>>>,
|
||||
files: Arc<std::sync::Mutex<BTreeMap<INodeNo, Item>>>,
|
||||
}
|
||||
|
||||
impl WatcherState {
|
||||
async fn run_loop(self) {
|
||||
loop {
|
||||
match self.transport.subscribe_events().await {
|
||||
Ok(mut stream) => {
|
||||
info!("network watcher: subscribed to /events");
|
||||
while let Some(item) = stream.next().await {
|
||||
match item {
|
||||
Ok(_event) => {
|
||||
if let Err(e) = self.reconcile_once().await {
|
||||
warn!(error = %e, "network watcher: reconcile after event failed");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(error = %e, "network watcher: stream error; reconnecting");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(error = %e, "network watcher: subscribe failed; will retry");
|
||||
}
|
||||
}
|
||||
tokio::time::sleep(POLL_FALLBACK_INTERVAL).await;
|
||||
if let Err(e) = self.reconcile_once().await {
|
||||
warn!(error = %e, "network watcher: poll reconcile failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn reconcile_once(&self) -> anyhow::Result<()> {
|
||||
let client_entries: Vec<(u64, u64)> = crate::db::entities::Entity::find()
|
||||
.all(&self.client)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|m| (m.inode as u64, m.hash as u64))
|
||||
.collect();
|
||||
|
||||
let response = self.transport.reconcile(client_entries).await?;
|
||||
|
||||
let mut changed_or_deleted: Vec<i64> =
|
||||
response.changed.iter().map(|ih| ih.inode as i64).collect();
|
||||
changed_or_deleted.extend(response.deleted.iter().map(|i| *i as i64));
|
||||
crate::db::cache::delete_cached_bytes_for(&changed_or_deleted, &self.client).await;
|
||||
|
||||
let wanted: Vec<u64> = response.changed.iter().map(|ih| ih.inode).collect();
|
||||
let mut current_manifest = self.latest_manifest.read().unwrap().clone();
|
||||
if !wanted.is_empty() {
|
||||
let entries = self.transport.get_metadata(wanted).await?;
|
||||
for entry in entries {
|
||||
current_manifest.insert(entry.id, entry);
|
||||
}
|
||||
}
|
||||
for inode in &response.deleted {
|
||||
current_manifest.remove(inode);
|
||||
}
|
||||
*self.latest_manifest.write().unwrap() = current_manifest.clone();
|
||||
|
||||
let _ = self.destination.clone();
|
||||
let _ = self.files.clone();
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user