pub mod vault;
pub mod nats;
pub mod memory;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;
use tokio::task::JoinHandle;
#[derive(Debug, Clone)]
pub enum ElectionError {
AlreadyLeader,
NotLeader,
Failed(String),
}
pub type ElectionResult<T> = Result<T, ElectionError>;
#[async_trait::async_trait]
pub trait LeaderElection: Send + Sync + 'static {
async fn become_leader(&mut self) -> ElectionResult<()>;
async fn is_leader(&self) -> bool;
async fn resign(&mut self) -> ElectionResult<()>;
async fn get_leader(&self) -> Option<String>;
async fn renew(&mut self) -> ElectionResult<()> {
Ok(())
}
}
pub fn spawn_renewal(
election: Arc<Mutex<dyn LeaderElection>>,
period: Duration,
) -> JoinHandle<()> {
tokio::spawn(async move {
let mut interval = tokio::time::interval(period);
interval.tick().await;
loop {
interval.tick().await;
let mut guard = election.lock().await;
if let Err(e) = guard.renew().await {
log::warn!("leader election renewal failed: {e:?}");
}
}
})
}
#[derive(Clone)]
pub struct LeaderHandle {
election: Arc<Mutex<dyn LeaderElection>>,
}
impl std::fmt::Debug for LeaderHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LeaderHandle").finish()
}
}
impl LeaderHandle {
pub fn new(election: Arc<Mutex<dyn LeaderElection>>) -> Self {
Self { election }
}
pub fn from_impl(election: impl LeaderElection) -> Self {
Self {
election: Arc::new(Mutex::new(election)),
}
}
pub async fn is_leader(&self) -> bool {
self.election.lock().await.is_leader().await
}
pub fn start_renewal_task(&self, period: Duration) -> JoinHandle<()> {
spawn_renewal(Arc::clone(&self.election), period)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::election::memory::MemoryElection;
#[tokio::test]
async fn test_renew_default_returns_ok() {
let mut election = MemoryElection::new("test-key", "inst-1");
election.renew().await.expect("default renew should return Ok");
}
#[tokio::test]
async fn test_leader_handle_from_impl() {
let mut election = MemoryElection::new("test-key", "inst-1");
election.become_leader().await.unwrap();
let handle = LeaderHandle::from_impl(election);
assert!(handle.is_leader().await);
}
}