use std::collections::HashMap;
use std::sync::{Arc, Mutex, OnceLock};
use futures::future::BoxFuture;
use crate::Result;
use crate::config::table::HudiTableConfig;
use crate::table::Table;
pub struct LockLease {
_inner: Box<dyn std::any::Any + Send>,
}
pub trait LockProvider: Send + Sync + std::fmt::Debug {
fn lock(&self) -> BoxFuture<'_, Result<LockLease>>;
}
#[derive(Debug)]
pub struct InProcessLockProvider {
base_path: String,
}
impl InProcessLockProvider {
pub fn new(base_path: impl Into<String>) -> Self {
Self {
base_path: base_path.into(),
}
}
fn lock_for_base_path(&self) -> Arc<tokio::sync::Mutex<()>> {
static LOCKS: OnceLock<Mutex<HashMap<String, Arc<tokio::sync::Mutex<()>>>>> =
OnceLock::new();
let registry = LOCKS.get_or_init(|| Mutex::new(HashMap::new()));
let mut map = match registry.lock() {
Ok(map) => map,
Err(poisoned) => poisoned.into_inner(),
};
map.entry(self.base_path.clone())
.or_insert_with(|| Arc::new(tokio::sync::Mutex::new(())))
.clone()
}
}
impl LockProvider for InProcessLockProvider {
fn lock(&self) -> BoxFuture<'_, Result<LockLease>> {
let lock = self.lock_for_base_path();
Box::pin(async move {
let guard = lock.lock_owned().await;
Ok(LockLease {
_inner: Box::new(guard),
})
})
}
}
pub(crate) fn lock_provider_for(table: &Table) -> Arc<dyn LockProvider> {
let base_path: String = table
.hudi_configs
.get_or_default(HudiTableConfig::BasePath)
.into();
Arc::new(InProcessLockProvider::new(base_path))
}