111 lines
3.6 KiB
Rust
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;
|
|
}
|
|
}
|
|
}
|