gitdataai/libs/git/hook/sync/lock.rs
ZhenYi ef61b193c4 fix(git/hook): refine Redis queue worker, remove dead code, fix warnings
- pool/mod.rs: pass shared http_client Arc to HookWorker
- worker.rs: remove double-locking (sync() manages its own lock),
  await all webhook handles before returning, share http_client,
  hoist namespace query out of loop
- redis.rs: atomic NAK via Lua script (LREM + LPUSH in one eval)
- sync/lock.rs: increase LOCK_TTL from 60s to 300s for large repos
- sync/mod.rs: split sync/sync_work, fsck_only/fsck_work, gc_only/gc_work
  so callers can choose locked vs lock-free path; run_gc + sync_skills
  outside the DB transaction
- hook/mod.rs: remove unused http field from HookService
- ssh/mod.rs, http/mod.rs: remove unused HookService/http imports
2026-04-17 13:05:07 +08:00

67 lines
2.1 KiB
Rust

use crate::GitError;
use crate::hook::sync::HookMetaDataSync;
impl HookMetaDataSync {
const LOCK_TTL_SECS: u64 = 300;
/// Try to acquire an exclusive lock for this repo.
/// Returns the lock value if acquired, which must be passed to `release_lock`.
pub async fn acquire_lock(&self) -> Result<String, GitError> {
let lock_key = format!("git:repo:lock:{}", self.repo.id);
let lock_value = format!("{}:{}", uuid::Uuid::new_v4(), std::process::id());
let mut conn = self
.cache
.conn()
.await
.map_err(|e| GitError::IoError(format!("failed to get redis connection: {}", e)))?;
let result: bool = redis::cmd("SET")
.arg(&lock_key)
.arg(&lock_value)
.arg("NX")
.arg("EX")
.arg(Self::LOCK_TTL_SECS)
.query_async(&mut conn)
.await
.map_err(|e| GitError::IoError(format!("failed to acquire lock: {}", e)))?;
if result {
Ok(lock_value)
} else {
Err(GitError::Locked(format!(
"repository {} is locked by another process",
self.repo.id
)))
}
}
/// Release the lock, but only if we still own it (value matches).
pub async fn release_lock(&self, lock_value: &str) -> Result<(), GitError> {
let lock_key = format!("git:repo:lock:{}", self.repo.id);
let mut conn = self
.cache
.conn()
.await
.map_err(|e| GitError::IoError(format!("failed to get redis connection: {}", e)))?;
let script = r#"
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
"#;
let _: i32 = redis::Script::new(script)
.key(&lock_key)
.arg(lock_value)
.invoke_async(&mut conn)
.await
.map_err(|e| GitError::IoError(format!("failed to release lock: {}", e)))?;
Ok(())
}
}