use deadpool_redis::cluster::Pool as RedisPool; pub async fn acquire_repo_lock( pool: &RedisPool, repo_id: uuid::Uuid, lock_value: &str, ttl_secs: usize, ) -> redis::RedisResult { let lock_key = format!("git:repo:lock:{}", repo_id); let redis = pool.get().await.map_err(|e| { redis::RedisError::from(( redis::ErrorKind::Io, "failed to get Redis connection", e.to_string(), )) })?; let mut conn: deadpool_redis::cluster::Connection = redis; let acquired: Option = redis::cmd("SET") .arg(&lock_key) .arg(lock_value) .arg("NX") .arg("EX") .arg(ttl_secs) .query_async(&mut conn) .await?; Ok(acquired.is_some()) } pub async fn release_repo_lock( pool: &RedisPool, repo_id: uuid::Uuid, lock_value: &str, ) { let lock_key = format!("git:repo:lock:{}", repo_id); let redis = match pool.get().await { Ok(c) => c, Err(e) => { tracing::warn!(error = %e, repo_id = %repo_id, "lock_release_redis_connection_failed"); return; } }; let mut conn: deadpool_redis::cluster::Connection = redis; let script = redis::Script::new( r#" if redis.call("GET", KEYS[1]) == ARGV[1] then redis.call("DEL", KEYS[1]) return 1 end return 0 "#, ); if let Err(e) = script .key(&lock_key) .arg(lock_value) .invoke_async::(&mut conn) .await { tracing::warn!(error = %e, repo_id = %repo_id, "lock_release_failed"); } }