photoncloud-monorepo/deployer/crates/node-agent/src/watcher.rs

111 lines
3.6 KiB
Rust

use std::time::Duration;
use anyhow::Result;
use chainfire_client::{Client, EventType};
use tracing::{info, warn};
#[derive(Debug, Clone, Copy)]
pub enum WatchScope {
Key,
Prefix,
}
#[derive(Debug, Clone)]
pub struct WatchChange {
pub event_type: EventType,
pub key: Vec<u8>,
pub value: Vec<u8>,
#[allow(dead_code)]
pub revision: u64,
}
pub struct ChainfireWatcher {
endpoint: String,
scope: WatchScope,
target: Vec<u8>,
reconnect_backoff: Duration,
}
impl ChainfireWatcher {
pub fn key(endpoint: String, key: Vec<u8>) -> Self {
Self {
endpoint,
scope: WatchScope::Key,
target: key,
reconnect_backoff: Duration::from_secs(1),
}
}
pub fn prefix(endpoint: String, prefix: Vec<u8>) -> Self {
Self {
endpoint,
scope: WatchScope::Prefix,
target: prefix,
reconnect_backoff: Duration::from_secs(1),
}
}
pub async fn watch_with_ready<F, G>(&self, mut on_connected: G, mut callback: F) -> Result<()>
where
F: FnMut(WatchChange) -> Result<()>,
G: FnMut() -> Result<()>,
{
loop {
match Client::connect(self.endpoint.clone()).await {
Ok(mut client) => {
let watch_result = match self.scope {
WatchScope::Key => client.watch(&self.target).await,
WatchScope::Prefix => client.watch_prefix(&self.target).await,
};
match watch_result {
Ok(mut handle) => {
info!(
scope = ?self.scope,
target = %String::from_utf8_lossy(&self.target),
watch_id = handle.id(),
"connected ChainFire watch"
);
on_connected()?;
while let Some(event) = handle.recv().await {
if let Err(error) = callback(WatchChange {
event_type: event.event_type,
key: event.key,
value: event.value,
revision: event.revision,
}) {
warn!(error = %error, "watch callback failed");
}
}
warn!(
scope = ?self.scope,
target = %String::from_utf8_lossy(&self.target),
"ChainFire watch stream ended; reconnecting"
);
}
Err(error) => {
warn!(
error = %error,
scope = ?self.scope,
target = %String::from_utf8_lossy(&self.target),
"failed to create ChainFire watch"
);
}
}
}
Err(error) => {
warn!(
error = %error,
scope = ?self.scope,
target = %String::from_utf8_lossy(&self.target),
"failed to connect ChainFire watch"
);
}
}
tokio::time::sleep(self.reconnect_backoff).await;
}
}
}