Files
rustfs/crates/lock/src/fast_lock/integration_example.rs
T
guojidan d4beb1cc0b Fix lock (#510)
* Refactor: reimplement lock

Signed-off-by: junxiang Mu <1948535941@qq.com>

* Fix: fix test case failed

Signed-off-by: junxiang Mu <1948535941@qq.com>

* Improve: lock pref

Signed-off-by: junxiang Mu <1948535941@qq.com>

* fix(lock): Fix resource cleanup issue when batch lock acquisition fails
Ensure that the locks already acquired are properly released when batch lock acquisition fails to avoid memory leaks
Improve the lock protection mechanism to prevent double release issues
Add complete Apache license declarations to all files

Signed-off-by: junxiang Mu <1948535941@qq.com>

---------

Signed-off-by: junxiang Mu <1948535941@qq.com>
2025-09-11 12:10:35 +08:00

256 lines
8.8 KiB
Rust

// Copyright 2024 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Example integration of FastObjectLockManager in set_disk.rs
// This shows how to replace the current slow lock system
use crate::fast_lock::{BatchLockRequest, FastObjectLockManager, ObjectLockRequest};
use std::sync::Arc;
use std::time::Duration;
/// Example integration into SetDisks structure
pub struct SetDisksWithFastLock {
/// Replace the old namespace_lock with fast lock manager
pub fast_lock_manager: Arc<FastObjectLockManager>,
pub locker_owner: String,
// ... other fields remain the same
}
impl SetDisksWithFastLock {
/// Example: Replace get_object_reader with fast locking
pub async fn get_object_reader_fast(
&self,
bucket: &str,
object: &str,
version: Option<&str>,
// ... other parameters
) -> Result<(), Box<dyn std::error::Error>> {
// Fast path: Try to acquire read lock immediately
let _read_guard = if let Some(v) = version {
// Version-specific lock
self.fast_lock_manager
.acquire_read_lock_versioned(bucket, object, v, self.locker_owner.as_str())
.await
.map_err(|_| "Lock acquisition failed")?
} else {
// Latest version lock
self.fast_lock_manager
.acquire_read_lock(bucket, object, self.locker_owner.as_str())
.await
.map_err(|_| "Lock acquisition failed")?
};
// Critical section: Read object
// The lock is automatically released when _read_guard goes out of scope
// ... actual read operation logic
Ok(())
}
/// Example: Replace put_object with fast locking
pub async fn put_object_fast(
&self,
bucket: &str,
object: &str,
version: Option<&str>,
// ... other parameters
) -> Result<(), Box<dyn std::error::Error>> {
// Acquire exclusive write lock with timeout
let request = ObjectLockRequest::new_write(bucket, object, self.locker_owner.as_str())
.with_acquire_timeout(Duration::from_secs(5))
.with_lock_timeout(Duration::from_secs(30));
let request = if let Some(v) = version {
request.with_version(v)
} else {
request
};
let _write_guard = self
.fast_lock_manager
.acquire_lock(request)
.await
.map_err(|_| "Lock acquisition failed")?;
// Critical section: Write object
// ... actual write operation logic
Ok(())
// Lock automatically released when _write_guard drops
}
/// Example: Replace delete_objects with batch fast locking
pub async fn delete_objects_fast(
&self,
bucket: &str,
objects: Vec<(&str, Option<&str>)>, // (object_name, version)
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
// Create batch request for atomic locking
let mut batch = BatchLockRequest::new(self.locker_owner.as_str()).with_all_or_nothing(true); // Either lock all or fail
// Add all objects to batch (sorted internally to prevent deadlocks)
for (object, version) in &objects {
let mut request = ObjectLockRequest::new_write(bucket, *object, self.locker_owner.as_str());
if let Some(v) = version {
request = request.with_version(*v);
}
batch.requests.push(request);
}
// Acquire all locks atomically
let batch_result = self.fast_lock_manager.acquire_locks_batch(batch).await;
if !batch_result.all_acquired {
return Err("Failed to acquire all locks for batch delete".into());
}
// Critical section: Delete all objects
let mut deleted = Vec::new();
for (object, _version) in objects {
// ... actual delete operation logic
deleted.push(object.to_string());
}
// All locks automatically released when guards go out of scope
Ok(deleted)
}
/// Example: Health check integration
pub fn get_lock_health(&self) -> crate::fast_lock::metrics::AggregatedMetrics {
self.fast_lock_manager.get_metrics()
}
/// Example: Cleanup integration
pub async fn cleanup_expired_locks(&self) -> usize {
self.fast_lock_manager.cleanup_expired().await
}
}
/// Performance comparison demonstration
pub mod performance_comparison {
use super::*;
use std::time::Instant;
pub async fn benchmark_fast_vs_old() {
let fast_manager = Arc::new(FastObjectLockManager::new());
let owner = "benchmark_owner";
// Benchmark fast lock acquisition
let start = Instant::now();
let mut guards = Vec::new();
for i in 0..1000 {
let guard = fast_manager
.acquire_write_lock("bucket", format!("object_{}", i), owner)
.await
.expect("Failed to acquire fast lock");
guards.push(guard);
}
let fast_duration = start.elapsed();
println!("Fast lock: 1000 acquisitions in {:?}", fast_duration);
// Release all
drop(guards);
// Compare with metrics
let metrics = fast_manager.get_metrics();
println!("Fast path rate: {:.2}%", metrics.shard_metrics.fast_path_rate() * 100.0);
println!("Average wait time: {:?}", metrics.shard_metrics.avg_wait_time());
println!("Total operations/sec: {:.2}", metrics.ops_per_second());
}
}
/// Migration guide from old to new system
pub mod migration_guide {
/*
Step-by-step migration from old lock system:
1. Replace namespace_lock field:
OLD: pub namespace_lock: Arc<rustfs_lock::NamespaceLock>
NEW: pub fast_lock_manager: Arc<FastObjectLockManager>
2. Replace lock acquisition:
OLD: self.namespace_lock.lock_guard(object, &self.locker_owner, timeout, ttl).await?
NEW: self.fast_lock_manager.acquire_write_lock(bucket, object, &self.locker_owner).await?
3. Replace read lock acquisition:
OLD: self.namespace_lock.rlock_guard(object, &self.locker_owner, timeout, ttl).await?
NEW: self.fast_lock_manager.acquire_read_lock(bucket, object, &self.locker_owner).await?
4. Add version support where needed:
NEW: self.fast_lock_manager.acquire_write_lock_versioned(bucket, object, version, owner).await?
5. Replace batch operations:
OLD: Multiple individual lock_guard calls in loop
NEW: Single BatchLockRequest with all objects
6. Remove manual lock release (RAII handles it automatically)
OLD: guard.disarm() or explicit release
NEW: Just let guard go out of scope
Expected performance improvements:
- 10-50x faster lock acquisition
- 90%+ fast path success rate
- Sub-millisecond lock operations
- No deadlock issues with batch operations
- Automatic cleanup and monitoring
*/
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_integration_example() {
let fast_manager = Arc::new(FastObjectLockManager::new());
let set_disks = SetDisksWithFastLock {
fast_lock_manager: fast_manager,
locker_owner: "test_owner".to_string(),
};
// Test read operation
assert!(set_disks.get_object_reader_fast("bucket", "object", None).await.is_ok());
// Test write operation
assert!(set_disks.put_object_fast("bucket", "object", Some("v1")).await.is_ok());
// Test batch delete
let objects = vec![("obj1", None), ("obj2", Some("v1"))];
let result = set_disks.delete_objects_fast("bucket", objects).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_version_locking() {
let fast_manager = Arc::new(FastObjectLockManager::new());
// Should be able to lock different versions simultaneously
let guard_v1 = fast_manager
.acquire_write_lock_versioned("bucket", "object", "v1", "owner1")
.await
.expect("Failed to lock v1");
let guard_v2 = fast_manager
.acquire_write_lock_versioned("bucket", "object", "v2", "owner2")
.await
.expect("Failed to lock v2");
// Both locks should coexist
assert!(!guard_v1.is_released());
assert!(!guard_v2.is_released());
}
}