Feature: lock support auto release

Signed-off-by: junxiang Mu <1948535941@qq.com>
This commit is contained in:
junxiang Mu
2025-08-09 17:52:08 +08:00
parent b391272e94
commit e369e9f481
5 changed files with 403 additions and 46 deletions
+142 -46
View File
@@ -3211,6 +3211,20 @@ impl ObjectIO for SetDisks {
h: HeaderMap,
opts: &ObjectOptions,
) -> Result<GetObjectReader> {
// Acquire a shared read-lock early to protect read consistency
let mut _read_lock_guard: Option<rustfs_lock::LockGuard> = None;
if !opts.no_lock {
let guard_opt = self
.namespace_lock
.rlock_guard(object, &self.locker_owner, Duration::from_secs(5), Duration::from_secs(10))
.await?;
if guard_opt.is_none() {
return Err(Error::other("can not get lock. please retry".to_string()));
}
_read_lock_guard = guard_opt;
}
let (fi, files, disks) = self
.get_object_fileinfo(bucket, object, opts, true)
.await
@@ -3256,7 +3270,10 @@ impl ObjectIO for SetDisks {
let object = object.to_owned();
let set_index = self.set_index;
let pool_index = self.pool_index;
// Move the read-lock guard into the task so it lives for the duration of the read
let _guard_to_hold = _read_lock_guard; // moved into closure below
tokio::spawn(async move {
let _guard = _guard_to_hold; // keep guard alive until task ends
if let Err(e) = Self::get_object_with_fileinfo(
&bucket,
&object,
@@ -3284,16 +3301,18 @@ impl ObjectIO for SetDisks {
async fn put_object(&self, bucket: &str, object: &str, data: &mut PutObjReader, opts: &ObjectOptions) -> Result<ObjectInfo> {
let disks = self.disks.read().await;
// Acquire per-object exclusive lock via RAII guard. It auto-releases asynchronously on drop.
let mut _object_lock_guard: Option<rustfs_lock::LockGuard> = None;
if !opts.no_lock {
let paths = vec![object.to_string()];
let lock_acquired = self
let guard_opt = self
.namespace_lock
.lock_batch(&paths, &self.locker_owner, Duration::from_secs(5), Duration::from_secs(10))
.lock_guard(object, &self.locker_owner, Duration::from_secs(5), Duration::from_secs(10))
.await?;
if !lock_acquired {
if guard_opt.is_none() {
return Err(Error::other("can not get lock. please retry".to_string()));
}
_object_lock_guard = guard_opt;
}
let mut user_defined = opts.user_defined.clone();
@@ -3500,14 +3519,6 @@ impl ObjectIO for SetDisks {
self.delete_all(RUSTFS_META_TMP_BUCKET, &tmp_dir).await?;
// Release lock if it was acquired
if !opts.no_lock {
let paths = vec![object.to_string()];
if let Err(err) = self.namespace_lock.unlock_batch(&paths, &self.locker_owner).await {
error!("Failed to unlock object {}: {}", object, err);
}
}
for (i, op_disk) in online_disks.iter().enumerate() {
if let Some(disk) = op_disk {
if disk.is_online().await {
@@ -3583,6 +3594,19 @@ impl StorageAPI for SetDisks {
return Err(StorageError::NotImplemented);
}
// Guard lock for source object metadata update
let mut _lock_guard: Option<rustfs_lock::LockGuard> = None;
{
let guard_opt = self
.namespace_lock
.lock_guard(src_object, &self.locker_owner, Duration::from_secs(5), Duration::from_secs(10))
.await?;
if guard_opt.is_none() {
return Err(Error::other("can not get lock. please retry".to_string()));
}
_lock_guard = guard_opt;
}
let disks = self.get_disks_internal().await;
let (mut metas, errs) = {
@@ -3676,6 +3700,18 @@ impl StorageAPI for SetDisks {
}
#[tracing::instrument(skip(self))]
async fn delete_object_version(&self, bucket: &str, object: &str, fi: &FileInfo, force_del_marker: bool) -> Result<()> {
// Guard lock for single object delete-version
let mut _lock_guard: Option<rustfs_lock::LockGuard> = None;
{
let guard_opt = self
.namespace_lock
.lock_guard(object, &self.locker_owner, Duration::from_secs(5), Duration::from_secs(10))
.await?;
if guard_opt.is_none() {
return Err(Error::other("can not get lock. please retry".to_string()));
}
_lock_guard = guard_opt;
}
let disks = self.get_disks(0, 0).await?;
let write_quorum = disks.len() / 2 + 1;
@@ -3732,6 +3768,23 @@ impl StorageAPI for SetDisks {
del_errs.push(None)
}
// Per-object guards to keep until function end
let mut _guards: Vec<Option<rustfs_lock::LockGuard>> = Vec::with_capacity(objects.len());
// Acquire locks for all objects first; mark errors for failures
for (i, dobj) in objects.iter().enumerate() {
match self
.namespace_lock
.lock_guard(&dobj.object_name, &self.locker_owner, Duration::from_secs(5), Duration::from_secs(10))
.await?
{
Some(g) => _guards.push(Some(g)),
None => {
del_errs[i] = Some(Error::other("can not get lock. please retry"));
_guards.push(None);
}
}
}
// let mut del_fvers = Vec::with_capacity(objects.len());
let mut vers_map: HashMap<&String, FileInfoVersions> = HashMap::new();
@@ -3788,7 +3841,10 @@ impl StorageAPI for SetDisks {
}
}
vers_map.insert(&dobj.object_name, v);
// Only add to vers_map if we hold the lock
if _guards[i].is_some() {
vers_map.insert(&dobj.object_name, v);
}
}
let mut vers = Vec::with_capacity(vers_map.len());
@@ -3830,6 +3886,18 @@ impl StorageAPI for SetDisks {
#[tracing::instrument(skip(self))]
async fn delete_object(&self, bucket: &str, object: &str, opts: ObjectOptions) -> Result<ObjectInfo> {
// Guard lock for single object delete
let mut _lock_guard: Option<rustfs_lock::LockGuard> = None;
if !opts.delete_prefix {
let guard_opt = self
.namespace_lock
.lock_guard(object, &self.locker_owner, Duration::from_secs(5), Duration::from_secs(10))
.await?;
if guard_opt.is_none() {
return Err(Error::other("can not get lock. please retry".to_string()));
}
_lock_guard = guard_opt;
}
if opts.delete_prefix {
self.delete_prefix(bucket, object)
.await
@@ -3952,33 +4020,18 @@ impl StorageAPI for SetDisks {
#[tracing::instrument(skip(self))]
async fn get_object_info(&self, bucket: &str, object: &str, opts: &ObjectOptions) -> Result<ObjectInfo> {
// let mut _ns = None;
// if !opts.no_lock {
// let paths = vec![object.to_string()];
// let ns_lock = new_nslock(
// Arc::clone(&self.ns_mutex),
// self.locker_owner.clone(),
// bucket.to_string(),
// paths,
// self.lockers.clone(),
// )
// .await;
// if !ns_lock
// .0
// .write()
// .await
// .get_lock(&Options {
// timeout: Duration::from_secs(5),
// retry_interval: Duration::from_secs(1),
// })
// .await
// .map_err(|err| Error::other(err.to_string()))?
// {
// return Err(Error::other("can not get lock. please retry".to_string()));
// }
// _ns = Some(ns_lock);
// }
// Acquire a shared read-lock to protect consistency during info fetch
let mut _read_lock_guard: Option<rustfs_lock::LockGuard> = None;
if !opts.no_lock {
let guard_opt = self
.namespace_lock
.rlock_guard(object, &self.locker_owner, Duration::from_secs(5), Duration::from_secs(10))
.await?;
if guard_opt.is_none() {
return Err(Error::other("can not get lock. please retry".to_string()));
}
_read_lock_guard = guard_opt;
}
let (fi, _, _) = self
.get_object_fileinfo(bucket, object, opts, false)
@@ -4010,6 +4063,19 @@ impl StorageAPI for SetDisks {
async fn put_object_metadata(&self, bucket: &str, object: &str, opts: &ObjectOptions) -> Result<ObjectInfo> {
// TODO: nslock
// Guard lock for metadata update
let mut _lock_guard: Option<rustfs_lock::LockGuard> = None;
if !opts.no_lock {
let guard_opt = self
.namespace_lock
.lock_guard(object, &self.locker_owner, Duration::from_secs(5), Duration::from_secs(10))
.await?;
if guard_opt.is_none() {
return Err(Error::other("can not get lock. please retry".to_string()));
}
_lock_guard = guard_opt;
}
let disks = self.get_disks_internal().await;
let (metas, errs) = {
@@ -4100,12 +4166,18 @@ impl StorageAPI for SetDisks {
}
};
/*if !opts.no_lock {
let lk = self.new_ns_lock(bucket, object);
let lkctx = lk.get_lock(globalDeleteOperationTimeout)?;
//ctx = lkctx.Context()
//defer lk.Unlock(lkctx)
}*/
// Acquire write-lock early; hold for the whole transition operation scope
let mut _lock_guard: Option<rustfs_lock::LockGuard> = None;
if !opts.no_lock {
let guard_opt = self
.namespace_lock
.lock_guard(object, &self.locker_owner, Duration::from_secs(5), Duration::from_secs(10))
.await?;
if guard_opt.is_none() {
return Err(Error::other("can not get lock. please retry".to_string()));
}
_lock_guard = guard_opt;
}
let (mut fi, meta_arr, online_disks) = self.get_object_fileinfo(bucket, object, opts, true).await?;
/*if err != nil {
@@ -4223,6 +4295,18 @@ impl StorageAPI for SetDisks {
#[tracing::instrument(level = "debug", skip(self))]
async fn restore_transitioned_object(&self, bucket: &str, object: &str, opts: &ObjectOptions) -> Result<()> {
// Acquire write-lock early for the restore operation
let mut _lock_guard: Option<rustfs_lock::LockGuard> = None;
if !opts.no_lock {
let guard_opt = self
.namespace_lock
.lock_guard(object, &self.locker_owner, Duration::from_secs(5), Duration::from_secs(10))
.await?;
if guard_opt.is_none() {
return Err(Error::other("can not get lock. please retry".to_string()));
}
_lock_guard = guard_opt;
}
let set_restore_header_fn = async move |oi: &mut ObjectInfo, rerr: Option<Error>| -> Result<()> {
if rerr.is_none() {
return Ok(());
@@ -4296,6 +4380,18 @@ impl StorageAPI for SetDisks {
#[tracing::instrument(level = "debug", skip(self))]
async fn put_object_tags(&self, bucket: &str, object: &str, tags: &str, opts: &ObjectOptions) -> Result<ObjectInfo> {
// Acquire write-lock for tag update (metadata write)
let mut _lock_guard: Option<rustfs_lock::LockGuard> = None;
if !opts.no_lock {
let guard_opt = self
.namespace_lock
.lock_guard(object, &self.locker_owner, Duration::from_secs(5), Duration::from_secs(10))
.await?;
if guard_opt.is_none() {
return Err(Error::other("can not get lock. please retry".to_string()));
}
_lock_guard = guard_opt;
}
let (mut fi, _, disks) = self.get_object_fileinfo(bucket, object, opts, false).await?;
fi.metadata.insert(AMZ_OBJECT_TAGGING.to_owned(), tags.to_owned());