use std::net::SocketAddr;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;
use tracing::info;
use crate::backend::{InMemoryBackend, LockBackend};
use crate::builder::LockBuilder;
use crate::clock::Clock;
use crate::error::{Error, Result};
#[derive(Clone, Debug)]
pub struct LockConfig {
pub cluster_seed: Vec<SocketAddr>,
pub default_ttl: Duration,
pub watchdog_interval: Duration,
}
impl Default for LockConfig {
fn default() -> Self {
Self {
cluster_seed: vec![],
default_ttl: Duration::from_secs(30),
watchdog_interval: Duration::from_secs(15),
}
}
}
pub struct ClientInner {
pub(crate) backend: Arc<dyn LockBackend>,
pub(crate) clock: Clock,
pub(crate) node_id: u64,
pub(crate) default_ttl: Duration,
pub(crate) watchdog_interval: Duration,
}
#[derive(Clone)]
pub struct LockClient {
inner: Arc<ClientInner>,
}
static NEXT_NODE_ID: AtomicU64 = AtomicU64::new(1);
impl LockClient {
pub async fn connect(config: LockConfig) -> Result<Self> {
Self::connect_with(config, Arc::new(InMemoryBackend::new())).await
}
pub async fn connect_with(config: LockConfig, backend: Arc<dyn LockBackend>) -> Result<Self> {
if config.default_ttl.is_zero() {
return Err(Error::Config("default_ttl must be non-zero".into()));
}
if config.watchdog_interval.is_zero() {
return Err(Error::Config("watchdog_interval must be non-zero".into()));
}
if config.watchdog_interval >= config.default_ttl {
return Err(Error::Config(
"watchdog_interval must be shorter than default_ttl".into(),
));
}
let node_id = NEXT_NODE_ID.fetch_add(1, Ordering::Relaxed);
info!(
node_id,
backend = backend.name(),
seeds = config.cluster_seed.len(),
"ash-lock client connected (discovery mocked)"
);
Ok(Self {
inner: Arc::new(ClientInner {
backend,
clock: Clock::new(),
node_id,
default_ttl: config.default_ttl,
watchdog_interval: config.watchdog_interval,
}),
})
}
pub fn node_id(&self) -> u64 {
self.inner.node_id
}
pub async fn lock(&self, resource: &str) -> LockBuilder<'_> {
let ttl = self.inner.default_ttl;
LockBuilder {
inner: &self.inner,
resource: resource.to_string(),
ttl,
fair: false,
wait_budget: ttl,
}
}
}