mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-06 05:17:42 +00:00
d4beb1cc0b
* 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>
256 lines
8.8 KiB
Rust
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());
|
|
}
|
|
}
|