use std::sync::OnceLock;
use redis::{Script, aio::ConnectionManager};
use crate::DistkitError;
use crate::lock::error::{validate_owner, validate_ttl};
const REFRESH_LUA: &str = r#"
if redis.call('GET', KEYS[1]) == ARGV[1] then
return redis.call('PEXPIRE', KEYS[1], ARGV[2])
else
return 0
end
"#;
const RELEASE_LUA: &str = r#"
if redis.call('GET', KEYS[1]) == ARGV[1] then
return redis.call('DEL', KEYS[1])
else
return 0
end
"#;
pub(crate) async fn acquire(
conn: &mut ConnectionManager,
key: &str,
owner: &str,
ttl_ms: i64,
) -> Result<bool, DistkitError> {
validate_owner(owner)?;
validate_ttl(ttl_ms)?;
let res: Option<String> = redis::cmd("SET")
.arg(key)
.arg(owner)
.arg("NX")
.arg("PX")
.arg(ttl_ms)
.query_async(conn)
.await?;
Ok(res.is_some())
}
pub(crate) async fn refresh(
conn: &mut ConnectionManager,
key: &str,
owner: &str,
ttl_ms: i64,
) -> Result<bool, DistkitError> {
validate_ttl(ttl_ms)?;
static REFRESH_SCRIPT: OnceLock<Script> = OnceLock::new();
let script = REFRESH_SCRIPT.get_or_init(|| Script::new(REFRESH_LUA));
let n: i64 = script
.key(key)
.arg(owner)
.arg(ttl_ms)
.invoke_async(conn)
.await?;
Ok(n == 1)
}
pub(crate) async fn release(
conn: &mut ConnectionManager,
key: &str,
owner: &str,
) -> Result<bool, DistkitError> {
static RELEASE_SCRIPT: OnceLock<Script> = OnceLock::new();
let script = RELEASE_SCRIPT.get_or_init(|| Script::new(RELEASE_LUA));
let n: i64 = script.key(key).arg(owner).invoke_async(conn).await?;
Ok(n == 1)
}