mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-10 23:26:53 +00:00
refactor(lock): restructure lock crate, remove unused modules and clarify directory layout
- Remove unused core/rwlock.rs and manager/ modules (ManagerFactory, LifecycleManager, NamespaceManager) - Move all lock-related code into crates/lock/src with clear submodules: client, core, utils, etc. - Ensure only necessary files and APIs are exposed, improve maintainability - No functional logic change, pure structure and cleanup refactor Signed-off-by: dandan <dandan@dandandeMac-Studio.local>
This commit is contained in:
@@ -0,0 +1,872 @@
|
||||
// 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.
|
||||
|
||||
|
||||
use dashmap::DashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::{
|
||||
client::LockClient,
|
||||
deadlock_detector::DeadlockDetector,
|
||||
error::Result,
|
||||
types::{
|
||||
DeadlockDetectionResult, LockId, LockInfo, LockRequest, LockResponse, LockStats, LockStatus, LockType,
|
||||
WaitQueueItem,
|
||||
},
|
||||
};
|
||||
|
||||
/// Local lock client
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LocalClient {
|
||||
/// Lock storage
|
||||
locks: Arc<DashMap<String, LockInfo>>,
|
||||
/// Deadlock detector
|
||||
deadlock_detector: Arc<Mutex<DeadlockDetector>>,
|
||||
/// Wait queues: resource -> wait queue
|
||||
wait_queues: Arc<DashMap<String, Vec<WaitQueueItem>>>,
|
||||
/// Statistics
|
||||
stats: Arc<Mutex<LockStats>>,
|
||||
}
|
||||
|
||||
impl LocalClient {
|
||||
/// Create new local client
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
locks: Arc::new(DashMap::new()),
|
||||
deadlock_detector: Arc::new(Mutex::new(DeadlockDetector::new())),
|
||||
wait_queues: Arc::new(DashMap::new()),
|
||||
stats: Arc::new(Mutex::new(LockStats::default())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Acquire lock with priority and deadlock detection
|
||||
async fn acquire_lock_with_priority(&self, request: LockRequest, lock_type: LockType) -> Result<LockResponse> {
|
||||
let _start_time = std::time::SystemTime::now();
|
||||
let lock_key = crate::utils::generate_lock_key(&request.resource, lock_type);
|
||||
|
||||
// Check deadlock detection
|
||||
if request.deadlock_detection {
|
||||
if let Ok(detection_result) = self.check_deadlock(&request).await {
|
||||
if detection_result.has_deadlock {
|
||||
return Ok(LockResponse::failure(
|
||||
format!("Deadlock detected: {:?}", detection_result.deadlock_cycle),
|
||||
crate::utils::duration_between(_start_time, std::time::SystemTime::now()),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Atomic check + insert
|
||||
match self.locks.entry(lock_key) {
|
||||
dashmap::mapref::entry::Entry::Occupied(mut entry) => {
|
||||
let existing = entry.get();
|
||||
if existing.owner != request.owner {
|
||||
// Add to wait queue
|
||||
let wait_item = WaitQueueItem::new(&request.owner, lock_type, request.priority);
|
||||
self.add_to_wait_queue(&request.resource, wait_item).await;
|
||||
|
||||
// Update deadlock detector
|
||||
self.update_deadlock_detector(&request, &existing.owner).await;
|
||||
|
||||
// Check wait timeout
|
||||
if let Some(wait_timeout) = request.wait_timeout {
|
||||
if crate::utils::duration_between(_start_time, std::time::SystemTime::now()) > wait_timeout {
|
||||
self.remove_from_wait_queue(&request.resource, &request.owner).await;
|
||||
return Ok(LockResponse::failure(
|
||||
"Wait timeout exceeded".to_string(),
|
||||
crate::utils::duration_between(_start_time, std::time::SystemTime::now()),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let position = self.get_wait_position(&request.resource, &request.owner).await;
|
||||
return Ok(LockResponse::waiting(
|
||||
crate::utils::duration_between(_start_time, std::time::SystemTime::now()),
|
||||
position,
|
||||
));
|
||||
}
|
||||
// Update lock info (same owner can re-acquire)
|
||||
let mut lock_info = existing.clone();
|
||||
lock_info.last_refreshed = std::time::SystemTime::now();
|
||||
lock_info.expires_at = std::time::SystemTime::now() + request.timeout;
|
||||
lock_info.priority = request.priority;
|
||||
entry.insert(lock_info.clone());
|
||||
Ok(LockResponse::success(
|
||||
lock_info,
|
||||
crate::utils::duration_between(_start_time, std::time::SystemTime::now()),
|
||||
))
|
||||
}
|
||||
dashmap::mapref::entry::Entry::Vacant(entry) => {
|
||||
// Insert new lock
|
||||
let lock_info = LockInfo {
|
||||
id: LockId::new(),
|
||||
resource: request.resource.clone(),
|
||||
lock_type,
|
||||
status: LockStatus::Acquired,
|
||||
owner: request.owner.clone(),
|
||||
acquired_at: std::time::SystemTime::now(),
|
||||
expires_at: std::time::SystemTime::now() + request.timeout,
|
||||
last_refreshed: std::time::SystemTime::now(),
|
||||
metadata: request.metadata.clone(),
|
||||
priority: request.priority,
|
||||
wait_start_time: None,
|
||||
};
|
||||
entry.insert(lock_info.clone());
|
||||
|
||||
// Update deadlock detector
|
||||
self.update_deadlock_detector(&request, "").await;
|
||||
|
||||
Ok(LockResponse::success(
|
||||
lock_info,
|
||||
crate::utils::duration_between(_start_time, std::time::SystemTime::now()),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check for deadlock
|
||||
async fn check_deadlock(&self, _request: &LockRequest) -> Result<DeadlockDetectionResult> {
|
||||
let mut detector = self.deadlock_detector.lock().await;
|
||||
Ok(detector.detect_deadlock())
|
||||
}
|
||||
|
||||
/// Update deadlock detector
|
||||
async fn update_deadlock_detector(&self, request: &LockRequest, current_owner: &str) {
|
||||
let mut detector = self.deadlock_detector.lock().await;
|
||||
|
||||
if !current_owner.is_empty() {
|
||||
// Add wait relationship
|
||||
detector.add_wait_relationship(
|
||||
&request.owner,
|
||||
&request.resource,
|
||||
vec![], // TODO: Get currently held resources
|
||||
request.priority,
|
||||
);
|
||||
}
|
||||
|
||||
// Update resource holder
|
||||
detector.update_resource_holder(&request.resource, &request.owner);
|
||||
}
|
||||
|
||||
/// Add to wait queue
|
||||
async fn add_to_wait_queue(&self, resource: &str, item: WaitQueueItem) {
|
||||
let mut queue = self.wait_queues.entry(resource.to_string()).or_default();
|
||||
queue.push(item);
|
||||
|
||||
// Sort by priority
|
||||
queue.sort_by(|a, b| b.priority.cmp(&a.priority));
|
||||
}
|
||||
|
||||
/// Remove from wait queue
|
||||
async fn remove_from_wait_queue(&self, resource: &str, owner: &str) {
|
||||
if let Some(mut queue) = self.wait_queues.get_mut(resource) {
|
||||
queue.retain(|item| item.owner != owner);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get wait position
|
||||
async fn get_wait_position(&self, resource: &str, owner: &str) -> usize {
|
||||
if let Some(queue) = self.wait_queues.get(resource) {
|
||||
for (i, item) in queue.iter().enumerate() {
|
||||
if item.owner == owner {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// Process wait queue
|
||||
async fn process_wait_queue(&self, resource: &str) {
|
||||
// Simple implementation to avoid never_loop warning
|
||||
if let Some(mut queue) = self.wait_queues.get_mut(resource) {
|
||||
if !queue.is_empty() {
|
||||
let _next_item = queue.remove(0);
|
||||
// TODO: Process next item in queue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Acquire multiple locks atomically
|
||||
pub async fn acquire_multiple_atomic(&self, requests: Vec<LockRequest>) -> Result<Vec<LockResponse>> {
|
||||
let mut responses = Vec::new();
|
||||
let mut acquired_locks = Vec::new();
|
||||
|
||||
for request in requests {
|
||||
match self.acquire_lock_with_priority(request.clone(), LockType::Exclusive).await {
|
||||
Ok(response) => {
|
||||
if response.is_success() {
|
||||
acquired_locks.push(request.resource.clone());
|
||||
}
|
||||
responses.push(response);
|
||||
}
|
||||
Err(e) => {
|
||||
// Rollback acquired locks
|
||||
for resource in acquired_locks {
|
||||
let _ = self.force_release_by_resource(&resource).await;
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(responses)
|
||||
}
|
||||
|
||||
/// Release multiple locks atomically
|
||||
pub async fn release_multiple_atomic(&self, lock_ids: Vec<LockId>) -> Result<Vec<bool>> {
|
||||
let mut results = Vec::new();
|
||||
for lock_id in lock_ids {
|
||||
results.push(self.release(&lock_id).await?);
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
/// Force release by resource
|
||||
async fn force_release_by_resource(&self, resource: &str) -> Result<bool> {
|
||||
let lock_key = crate::utils::generate_lock_key(resource, LockType::Exclusive);
|
||||
if let Some((_, lock_info)) = self.locks.remove(&lock_key) {
|
||||
// Update statistics
|
||||
let mut stats = self.stats.lock().await;
|
||||
stats.total_releases += 1;
|
||||
stats.total_hold_time += crate::utils::duration_between(lock_info.acquired_at, std::time::SystemTime::now());
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check multiple lock status
|
||||
pub async fn check_multiple_status(&self, lock_ids: Vec<LockId>) -> Result<Vec<Option<LockInfo>>> {
|
||||
let mut results = Vec::new();
|
||||
for lock_id in lock_ids {
|
||||
results.push(self.check_status(&lock_id).await?);
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
/// Refresh multiple locks atomically
|
||||
pub async fn refresh_multiple_atomic(&self, lock_ids: Vec<LockId>) -> Result<Vec<bool>> {
|
||||
let mut results = Vec::new();
|
||||
for lock_id in lock_ids {
|
||||
results.push(self.refresh(&lock_id).await?);
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
/// Get deadlock statistics
|
||||
pub async fn get_deadlock_stats(&self) -> Result<(usize, std::time::SystemTime)> {
|
||||
let detector = self.deadlock_detector.lock().await;
|
||||
let (count, time) = detector.get_stats();
|
||||
Ok((count, time))
|
||||
}
|
||||
|
||||
/// Detect deadlock
|
||||
pub async fn detect_deadlock(&self) -> Result<DeadlockDetectionResult> {
|
||||
let mut detector = self.deadlock_detector.lock().await;
|
||||
Ok(detector.detect_deadlock())
|
||||
}
|
||||
|
||||
/// Cleanup expired waits
|
||||
pub async fn cleanup_expired_waits(&self, max_wait_time: std::time::Duration) {
|
||||
let now = std::time::SystemTime::now();
|
||||
for mut queue in self.wait_queues.iter_mut() {
|
||||
queue.retain(|item| now.duration_since(item.wait_start_time).unwrap_or_default() <= max_wait_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LocalClient {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl super::LockClient for LocalClient {
|
||||
async fn acquire_exclusive(&self, request: LockRequest) -> Result<LockResponse> {
|
||||
self.acquire_lock_with_priority(request, LockType::Exclusive).await
|
||||
}
|
||||
|
||||
async fn acquire_shared(&self, request: LockRequest) -> Result<LockResponse> {
|
||||
self.acquire_lock_with_priority(request, LockType::Shared).await
|
||||
}
|
||||
|
||||
async fn release(&self, lock_id: &LockId) -> Result<bool> {
|
||||
let _start_time = std::time::SystemTime::now();
|
||||
|
||||
// Find and remove the lock
|
||||
let mut found = false;
|
||||
let mut lock_info_opt = None;
|
||||
|
||||
for entry in self.locks.iter() {
|
||||
if entry.id == *lock_id {
|
||||
lock_info_opt = Some(entry.clone());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
let lock_key = crate::utils::generate_lock_key(
|
||||
&lock_info_opt.as_ref().unwrap().resource,
|
||||
lock_info_opt.as_ref().unwrap().lock_type,
|
||||
);
|
||||
if let Some((_, lock_info)) = self.locks.remove(&lock_key) {
|
||||
// Update statistics
|
||||
let mut stats = self.stats.lock().await;
|
||||
stats.total_releases += 1;
|
||||
stats.total_hold_time += crate::utils::duration_between(lock_info.acquired_at, std::time::SystemTime::now());
|
||||
|
||||
// Process wait queue
|
||||
self.process_wait_queue(&lock_info.resource).await;
|
||||
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
async fn refresh(&self, lock_id: &LockId) -> Result<bool> {
|
||||
for mut entry in self.locks.iter_mut() {
|
||||
if entry.id == *lock_id {
|
||||
entry.last_refreshed = std::time::SystemTime::now();
|
||||
entry.expires_at = std::time::SystemTime::now() + std::time::Duration::from_secs(30);
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
async fn force_release(&self, lock_id: &LockId) -> Result<bool> {
|
||||
self.release(lock_id).await
|
||||
}
|
||||
|
||||
async fn check_status(&self, lock_id: &LockId) -> Result<Option<LockInfo>> {
|
||||
for entry in self.locks.iter() {
|
||||
if entry.id == *lock_id {
|
||||
// Check if lock has expired
|
||||
if entry.expires_at < std::time::SystemTime::now() {
|
||||
// Lock has expired, remove it
|
||||
let lock_key = crate::utils::generate_lock_key(&entry.resource, entry.lock_type);
|
||||
let _ = self.locks.remove(&lock_key);
|
||||
return Ok(None);
|
||||
}
|
||||
return Ok(Some(entry.clone()));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn get_stats(&self) -> Result<LockStats> {
|
||||
let mut stats = self.stats.lock().await;
|
||||
stats.total_locks = self.locks.len();
|
||||
stats.total_wait_queues = self.wait_queues.len();
|
||||
|
||||
// Calculate average hold time
|
||||
if stats.total_releases > 0 {
|
||||
stats.average_hold_time =
|
||||
std::time::Duration::from_secs(stats.total_hold_time.as_secs() / stats.total_releases as u64);
|
||||
}
|
||||
|
||||
Ok(stats.clone())
|
||||
}
|
||||
|
||||
async fn close(&self) -> Result<()> {
|
||||
// Cleanup all locks
|
||||
self.locks.clear();
|
||||
self.wait_queues.clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn is_online(&self) -> bool {
|
||||
true // Local client is always online
|
||||
}
|
||||
|
||||
async fn is_local(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::types::{LockMetadata, LockPriority, LockType};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_local_client_acquire_exclusive() {
|
||||
let client = LocalClient::new();
|
||||
let request = LockRequest {
|
||||
resource: "test_resource".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "test_owner".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
|
||||
let response = client.acquire_exclusive(request).await.unwrap();
|
||||
assert!(response.is_success());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_local_client_acquire_shared() {
|
||||
let client = LocalClient::new();
|
||||
let request = LockRequest {
|
||||
resource: "test_resource".to_string(),
|
||||
lock_type: LockType::Shared,
|
||||
owner: "test_owner".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
|
||||
let response = client.acquire_shared(request).await.unwrap();
|
||||
assert!(response.is_success());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_local_client_release() {
|
||||
let client = LocalClient::new();
|
||||
let request = LockRequest {
|
||||
resource: "test_resource".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "test_owner".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
|
||||
let response = client.acquire_exclusive(request).await.unwrap();
|
||||
assert!(response.is_success());
|
||||
|
||||
let lock_id = &response.lock_info().unwrap().id;
|
||||
let result = client.release(lock_id).await.unwrap();
|
||||
assert!(result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_local_client_concurrent_access() {
|
||||
let client = Arc::new(LocalClient::new());
|
||||
let mut handles = vec![];
|
||||
|
||||
for i in 0..10 {
|
||||
let client_clone = client.clone();
|
||||
let handle = tokio::spawn(async move {
|
||||
let request = LockRequest {
|
||||
resource: "concurrent_resource".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: format!("owner_{i}"),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
|
||||
let response = client_clone.acquire_exclusive(request).await.unwrap();
|
||||
if response.is_success() {
|
||||
let lock_id = &response.lock_info().unwrap().id;
|
||||
let _ = client_clone.release(lock_id).await;
|
||||
}
|
||||
});
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
for handle in handles {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dashmap_performance() {
|
||||
let client = LocalClient::new();
|
||||
let start_time = std::time::Instant::now();
|
||||
|
||||
// Simulate high concurrent access
|
||||
let mut handles = vec![];
|
||||
for i in 0..100 {
|
||||
let client_clone = Arc::new(client.clone());
|
||||
let handle = tokio::spawn(async move {
|
||||
let request = LockRequest {
|
||||
resource: format!("resource_{i}"),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: format!("owner_{i}"),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
|
||||
let response = client_clone.acquire_exclusive(request).await.unwrap();
|
||||
if response.is_success() {
|
||||
let lock_id = &response.lock_info().unwrap().id;
|
||||
let _ = client_clone.release(lock_id).await;
|
||||
}
|
||||
});
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
for handle in handles {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
|
||||
let duration = start_time.elapsed();
|
||||
println!("DashMap performance test completed in {duration:?}");
|
||||
assert!(duration < std::time::Duration::from_secs(5));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_atomic_operations() {
|
||||
let client = LocalClient::new();
|
||||
let request = LockRequest {
|
||||
resource: "atomic_resource".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "test_owner".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
|
||||
// Test atomic acquire
|
||||
let response = client.acquire_exclusive(request).await.unwrap();
|
||||
assert!(response.is_success());
|
||||
|
||||
// Test concurrent access to same resource
|
||||
let client_clone = Arc::new(client);
|
||||
let mut handles = vec![];
|
||||
for i in 0..5 {
|
||||
let client_clone = client_clone.clone();
|
||||
let handle = tokio::spawn(async move {
|
||||
let request = LockRequest {
|
||||
resource: "atomic_resource".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: format!("owner_{i}"),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
|
||||
let response = client_clone.acquire_exclusive(request).await.unwrap();
|
||||
response.is_waiting() // Should be waiting due to atomic operation
|
||||
});
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
for handle in handles {
|
||||
let result = handle.await.unwrap();
|
||||
assert!(result);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_batch_atomic_operations() {
|
||||
let client = LocalClient::new();
|
||||
let requests = vec![
|
||||
LockRequest {
|
||||
resource: "batch_resource_1".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "owner_1".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
},
|
||||
LockRequest {
|
||||
resource: "batch_resource_2".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "owner_1".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
},
|
||||
];
|
||||
|
||||
let responses = client.acquire_multiple_atomic(requests).await.unwrap();
|
||||
assert_eq!(responses.len(), 2);
|
||||
assert!(responses[0].is_success());
|
||||
assert!(responses[1].is_success());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_batch_atomic_rollback() {
|
||||
let client = LocalClient::new();
|
||||
|
||||
// First acquire a lock
|
||||
let first_request = LockRequest {
|
||||
resource: "rollback_resource".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "owner_1".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
let response = client.acquire_exclusive(first_request).await.unwrap();
|
||||
assert!(response.is_success());
|
||||
|
||||
// Try to acquire same resource in batch (should fail and rollback)
|
||||
let requests = vec![
|
||||
LockRequest {
|
||||
resource: "rollback_resource".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "owner_2".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
},
|
||||
LockRequest {
|
||||
resource: "rollback_resource_2".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "owner_2".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
},
|
||||
];
|
||||
|
||||
let responses = client.acquire_multiple_atomic(requests).await.unwrap();
|
||||
assert_eq!(responses.len(), 2);
|
||||
assert!(responses[0].is_waiting()); // Should be waiting
|
||||
assert!(responses[1].is_success()); // Second should succeed
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_concurrent_atomic_operations() {
|
||||
let client = Arc::new(LocalClient::new());
|
||||
let mut handles = vec![];
|
||||
|
||||
for i in 0..10 {
|
||||
let client_clone = client.clone();
|
||||
let handle = tokio::spawn(async move {
|
||||
let requests = vec![
|
||||
LockRequest {
|
||||
resource: format!("concurrent_batch_{i}"),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: format!("owner_{i}"),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
},
|
||||
LockRequest {
|
||||
resource: format!("concurrent_batch_{i}_2"),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: format!("owner_{i}"),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
},
|
||||
];
|
||||
|
||||
let responses = client_clone.acquire_multiple_atomic(requests).await.unwrap();
|
||||
assert_eq!(responses.len(), 2);
|
||||
|
||||
// Release locks
|
||||
for response in responses {
|
||||
if response.is_success() {
|
||||
let lock_id = &response.lock_info().unwrap().id;
|
||||
let _ = client_clone.release(lock_id).await;
|
||||
}
|
||||
}
|
||||
});
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
for handle in handles {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_priority_upgrade() {
|
||||
let client = LocalClient::new();
|
||||
|
||||
// Acquire lock with normal priority
|
||||
let normal_request = LockRequest {
|
||||
resource: "priority_resource".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "normal_owner".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
let response = client.acquire_exclusive(normal_request).await.unwrap();
|
||||
assert!(response.is_success());
|
||||
|
||||
// Try to acquire with high priority (should be waiting)
|
||||
let high_request = LockRequest {
|
||||
resource: "priority_resource".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "high_owner".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::High,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
let response = client.acquire_exclusive(high_request.clone()).await.unwrap();
|
||||
assert!(response.is_waiting());
|
||||
|
||||
// Release normal priority lock
|
||||
let lock_id = &response.lock_info().unwrap().id;
|
||||
let _ = client.release(lock_id).await;
|
||||
|
||||
// High priority should now acquire
|
||||
let response = client.acquire_exclusive(high_request).await.unwrap();
|
||||
assert!(response.is_success());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_deadlock_detection() {
|
||||
let client = LocalClient::new();
|
||||
|
||||
// Create a potential deadlock scenario
|
||||
let request1 = LockRequest {
|
||||
resource: "resource_a".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "owner_1".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: true,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
|
||||
let request2 = LockRequest {
|
||||
resource: "resource_b".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "owner_2".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: true,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
|
||||
// Acquire first lock
|
||||
let response1 = client.acquire_exclusive(request1).await.unwrap();
|
||||
assert!(response1.is_success());
|
||||
|
||||
// Acquire second lock
|
||||
let response2 = client.acquire_exclusive(request2).await.unwrap();
|
||||
assert!(response2.is_success());
|
||||
|
||||
// Try to create deadlock
|
||||
let deadlock_request1 = LockRequest {
|
||||
resource: "resource_b".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "owner_1".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: true,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
|
||||
let response = client.acquire_exclusive(deadlock_request1).await.unwrap();
|
||||
assert!(response.is_waiting() || response.is_failure());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_wait_timeout() {
|
||||
let client = LocalClient::new();
|
||||
|
||||
// Acquire lock
|
||||
let request1 = LockRequest {
|
||||
resource: "timeout_resource".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "owner_1".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: None,
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
let response = client.acquire_exclusive(request1).await.unwrap();
|
||||
assert!(response.is_success());
|
||||
|
||||
// Try to acquire with short wait timeout
|
||||
let request2 = LockRequest {
|
||||
resource: "timeout_resource".to_string(),
|
||||
lock_type: LockType::Exclusive,
|
||||
owner: "owner_2".to_string(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
wait_timeout: Some(std::time::Duration::from_millis(100)),
|
||||
priority: LockPriority::Normal,
|
||||
deadlock_detection: false,
|
||||
metadata: LockMetadata::default(),
|
||||
};
|
||||
|
||||
let start_time = std::time::Instant::now();
|
||||
let response = client.acquire_exclusive(request2).await.unwrap();
|
||||
let duration = start_time.elapsed();
|
||||
|
||||
assert!(response.is_failure() || response.is_waiting());
|
||||
assert!(duration < std::time::Duration::from_secs(1));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_deadlock_stats() {
|
||||
let client = LocalClient::new();
|
||||
|
||||
let (count, last_time) = client.get_deadlock_stats().await.unwrap();
|
||||
assert_eq!(count, 0);
|
||||
assert!(last_time < std::time::SystemTime::now());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cleanup_expired_waits() {
|
||||
let client = LocalClient::new();
|
||||
|
||||
// Add some wait items
|
||||
let wait_item = WaitQueueItem::new("test_owner", LockType::Exclusive, LockPriority::Normal);
|
||||
client.add_to_wait_queue("test_resource", wait_item).await;
|
||||
|
||||
// Cleanup with short timeout
|
||||
client.cleanup_expired_waits(std::time::Duration::from_millis(1)).await;
|
||||
|
||||
// Wait queue should be empty
|
||||
let position = client.get_wait_position("test_resource", "test_owner").await;
|
||||
assert_eq!(position, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// 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.
|
||||
|
||||
pub mod local;
|
||||
pub mod remote;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
error::Result,
|
||||
types::{LockId, LockInfo, LockRequest, LockResponse, LockStats},
|
||||
};
|
||||
|
||||
/// Lock client trait
|
||||
#[async_trait]
|
||||
pub trait LockClient: Send + Sync {
|
||||
/// Acquire exclusive lock
|
||||
async fn acquire_exclusive(&self, request: LockRequest) -> Result<LockResponse>;
|
||||
|
||||
/// Acquire shared lock
|
||||
async fn acquire_shared(&self, request: LockRequest) -> Result<LockResponse>;
|
||||
|
||||
/// Release lock
|
||||
async fn release(&self, lock_id: &LockId) -> Result<bool>;
|
||||
|
||||
/// Refresh lock
|
||||
async fn refresh(&self, lock_id: &LockId) -> Result<bool>;
|
||||
|
||||
/// Force release lock
|
||||
async fn force_release(&self, lock_id: &LockId) -> Result<bool>;
|
||||
|
||||
/// Check lock status
|
||||
async fn check_status(&self, lock_id: &LockId) -> Result<Option<LockInfo>>;
|
||||
|
||||
/// Get statistics
|
||||
async fn get_stats(&self) -> Result<LockStats>;
|
||||
|
||||
/// Close client
|
||||
async fn close(&self) -> Result<()>;
|
||||
|
||||
/// Check if client is online
|
||||
async fn is_online(&self) -> bool;
|
||||
|
||||
/// Check if client is local
|
||||
async fn is_local(&self) -> bool;
|
||||
}
|
||||
|
||||
/// Client factory
|
||||
pub struct ClientFactory;
|
||||
|
||||
impl ClientFactory {
|
||||
/// Create local client
|
||||
pub fn create_local() -> Arc<dyn LockClient> {
|
||||
Arc::new(local::LocalClient::new())
|
||||
}
|
||||
|
||||
/// Create remote client
|
||||
pub fn create_remote(endpoint: String) -> Arc<dyn LockClient> {
|
||||
Arc::new(remote::RemoteClient::new(endpoint))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::types::LockType;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_client_factory() {
|
||||
let local_client = ClientFactory::create_local();
|
||||
assert!(local_client.is_local().await);
|
||||
|
||||
let remote_client = ClientFactory::create_remote("http://localhost:8080".to_string());
|
||||
assert!(!remote_client.is_local().await);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_local_client_basic_operations() {
|
||||
let client = ClientFactory::create_local();
|
||||
|
||||
let request = crate::types::LockRequest::new("test-resource", LockType::Exclusive, "test-owner");
|
||||
|
||||
// Test lock acquisition
|
||||
let response = client.acquire_exclusive(request).await;
|
||||
assert!(response.is_ok());
|
||||
|
||||
if let Ok(response) = response {
|
||||
if response.success {
|
||||
let lock_info = response.lock_info.unwrap();
|
||||
|
||||
// Test status check
|
||||
let status = client.check_status(&lock_info.id).await;
|
||||
assert!(status.is_ok());
|
||||
assert!(status.unwrap().is_some());
|
||||
|
||||
// Test lock release
|
||||
let released = client.release(&lock_info.id).await;
|
||||
assert!(released.is_ok());
|
||||
assert!(released.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
// 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.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use rustfs_protos::{node_service_time_out_client, proto_gen::node_service::GenerallyLockRequest};
|
||||
use tonic::Request;
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
error::{LockError, Result},
|
||||
lock_args::LockArgs,
|
||||
types::{LockId, LockInfo, LockRequest, LockResponse, LockStats},
|
||||
};
|
||||
|
||||
/// Remote lock client
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RemoteClient {
|
||||
addr: String,
|
||||
}
|
||||
|
||||
impl RemoteClient {
|
||||
/// Create new remote client from endpoint string (for trait兼容)
|
||||
pub fn new(endpoint: String) -> Self {
|
||||
Self { addr: endpoint }
|
||||
}
|
||||
/// Create new remote client from url::Url(兼容 namespace/distributed 场景)
|
||||
pub fn from_url(url: url::Url) -> Self {
|
||||
let addr = format!("{}://{}:{}", url.scheme(), url.host_str().unwrap(), url.port().unwrap());
|
||||
Self { addr }
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助方法:从 LockRequest 创建 LockArgs
|
||||
impl LockArgs {
|
||||
fn from_request(request: &LockRequest, _is_shared: bool) -> Self {
|
||||
Self {
|
||||
uid: uuid::Uuid::new_v4().to_string(),
|
||||
resources: vec![request.resource.clone()],
|
||||
owner: request.owner.clone(),
|
||||
source: "remote_client".to_string(),
|
||||
quorum: 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_lock_id(lock_id: &LockId) -> Self {
|
||||
Self {
|
||||
uid: lock_id.to_string(),
|
||||
resources: vec![],
|
||||
owner: "remote_client".to_string(),
|
||||
source: "remote_client".to_string(),
|
||||
quorum: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl super::LockClient for RemoteClient {
|
||||
async fn acquire_exclusive(&self, request: LockRequest) -> Result<LockResponse> {
|
||||
info!("remote acquire_exclusive");
|
||||
let args = LockArgs::from_request(&request, false);
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| LockError::internal(format!("can not get client, err: {err}")))?;
|
||||
let req = Request::new(GenerallyLockRequest { args: serde_json::to_string(&args).map_err(|e| LockError::internal(format!("Failed to serialize args: {e}")))? });
|
||||
let resp = client.lock(req).await.map_err(|e| LockError::internal(e.to_string()))?.into_inner();
|
||||
if let Some(error_info) = resp.error_info {
|
||||
return Err(LockError::internal(error_info));
|
||||
}
|
||||
Ok(LockResponse {
|
||||
success: resp.success,
|
||||
lock_info: None, // 可扩展: 解析resp内容
|
||||
error: None,
|
||||
wait_time: std::time::Duration::ZERO,
|
||||
position_in_queue: None,
|
||||
})
|
||||
}
|
||||
async fn acquire_shared(&self, request: LockRequest) -> Result<LockResponse> {
|
||||
info!("remote acquire_shared");
|
||||
let args = LockArgs::from_request(&request, true);
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| LockError::internal(format!("can not get client, err: {err}")))?;
|
||||
let req = Request::new(GenerallyLockRequest { args: serde_json::to_string(&args).map_err(|e| LockError::internal(format!("Failed to serialize args: {e}")))? });
|
||||
let resp = client.r_lock(req).await.map_err(|e| LockError::internal(e.to_string()))?.into_inner();
|
||||
if let Some(error_info) = resp.error_info {
|
||||
return Err(LockError::internal(error_info));
|
||||
}
|
||||
Ok(LockResponse {
|
||||
success: resp.success,
|
||||
lock_info: None,
|
||||
error: None,
|
||||
wait_time: std::time::Duration::ZERO,
|
||||
position_in_queue: None,
|
||||
})
|
||||
}
|
||||
async fn release(&self, lock_id: &LockId) -> Result<bool> {
|
||||
info!("remote release");
|
||||
let args = LockArgs::from_lock_id(lock_id);
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| LockError::internal(format!("can not get client, err: {err}")))?;
|
||||
let req = Request::new(GenerallyLockRequest { args: serde_json::to_string(&args).map_err(|e| LockError::internal(format!("Failed to serialize args: {e}")))? });
|
||||
let resp = client.un_lock(req).await.map_err(|e| LockError::internal(e.to_string()))?.into_inner();
|
||||
if let Some(error_info) = resp.error_info {
|
||||
return Err(LockError::internal(error_info));
|
||||
}
|
||||
Ok(resp.success)
|
||||
}
|
||||
async fn refresh(&self, lock_id: &LockId) -> Result<bool> {
|
||||
info!("remote refresh");
|
||||
let args = LockArgs::from_lock_id(lock_id);
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| LockError::internal(format!("can not get client, err: {err}")))?;
|
||||
let req = Request::new(GenerallyLockRequest { args: serde_json::to_string(&args).map_err(|e| LockError::internal(format!("Failed to serialize args: {e}")))? });
|
||||
let resp = client.refresh(req).await.map_err(|e| LockError::internal(e.to_string()))?.into_inner();
|
||||
if let Some(error_info) = resp.error_info {
|
||||
return Err(LockError::internal(error_info));
|
||||
}
|
||||
Ok(resp.success)
|
||||
}
|
||||
async fn force_release(&self, lock_id: &LockId) -> Result<bool> {
|
||||
info!("remote force_release");
|
||||
let args = LockArgs::from_lock_id(lock_id);
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| LockError::internal(format!("can not get client, err: {err}")))?;
|
||||
let req = Request::new(GenerallyLockRequest { args: serde_json::to_string(&args).map_err(|e| LockError::internal(format!("Failed to serialize args: {e}")))? });
|
||||
let resp = client.force_un_lock(req).await.map_err(|e| LockError::internal(e.to_string()))?.into_inner();
|
||||
if let Some(error_info) = resp.error_info {
|
||||
return Err(LockError::internal(error_info));
|
||||
}
|
||||
Ok(resp.success)
|
||||
}
|
||||
async fn check_status(&self, _lock_id: &LockId) -> Result<Option<LockInfo>> {
|
||||
// 可扩展: 实现远程状态查询
|
||||
Ok(None)
|
||||
}
|
||||
async fn get_stats(&self) -> Result<LockStats> {
|
||||
// 可扩展: 实现远程统计
|
||||
Ok(LockStats::default())
|
||||
}
|
||||
async fn close(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
async fn is_online(&self) -> bool {
|
||||
true
|
||||
}
|
||||
async fn is_local(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// 同时实现 Locker trait 以兼容现有调用
|
||||
#[async_trait]
|
||||
impl crate::Locker for RemoteClient {
|
||||
async fn lock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
info!("remote lock");
|
||||
let args = serde_json::to_string(args).map_err(|e| LockError::internal(format!("Failed to serialize args: {e}")))?;
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| LockError::internal(format!("can not get client, err: {err}")))?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client
|
||||
.lock(request)
|
||||
.await
|
||||
.map_err(|e| LockError::internal(e.to_string()))?
|
||||
.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(LockError::internal(error_info));
|
||||
}
|
||||
|
||||
Ok(response.success)
|
||||
}
|
||||
|
||||
async fn unlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
info!("remote unlock");
|
||||
let args = serde_json::to_string(args).map_err(|e| LockError::internal(format!("Failed to serialize args: {e}")))?;
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| LockError::internal(format!("can not get client, err: {err}")))?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client
|
||||
.un_lock(request)
|
||||
.await
|
||||
.map_err(|e| LockError::internal(e.to_string()))?
|
||||
.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(LockError::internal(error_info));
|
||||
}
|
||||
|
||||
Ok(response.success)
|
||||
}
|
||||
|
||||
async fn rlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
info!("remote rlock");
|
||||
let args = serde_json::to_string(args).map_err(|e| LockError::internal(format!("Failed to serialize args: {e}")))?;
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| LockError::internal(format!("can not get client, err: {err}")))?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client
|
||||
.r_lock(request)
|
||||
.await
|
||||
.map_err(|e| LockError::internal(e.to_string()))?
|
||||
.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(LockError::internal(error_info));
|
||||
}
|
||||
|
||||
Ok(response.success)
|
||||
}
|
||||
|
||||
async fn runlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
info!("remote runlock");
|
||||
let args = serde_json::to_string(args).map_err(|e| LockError::internal(format!("Failed to serialize args: {e}")))?;
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| LockError::internal(format!("can not get client, err: {err}")))?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client
|
||||
.r_un_lock(request)
|
||||
.await
|
||||
.map_err(|e| LockError::internal(e.to_string()))?
|
||||
.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(LockError::internal(error_info));
|
||||
}
|
||||
|
||||
Ok(response.success)
|
||||
}
|
||||
|
||||
async fn refresh(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
info!("remote refresh");
|
||||
let args = serde_json::to_string(args).map_err(|e| LockError::internal(format!("Failed to serialize args: {e}")))?;
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| LockError::internal(format!("can not get client, err: {err}")))?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client
|
||||
.refresh(request)
|
||||
.await
|
||||
.map_err(|e| LockError::internal(e.to_string()))?
|
||||
.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(LockError::internal(error_info));
|
||||
}
|
||||
|
||||
Ok(response.success)
|
||||
}
|
||||
|
||||
async fn force_unlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
info!("remote force_unlock");
|
||||
let args = serde_json::to_string(args).map_err(|e| LockError::internal(format!("Failed to serialize args: {e}")))?;
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| LockError::internal(format!("can not get client, err: {err}")))?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client
|
||||
.force_un_lock(request)
|
||||
.await
|
||||
.map_err(|e| LockError::internal(e.to_string()))?
|
||||
.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(LockError::internal(error_info));
|
||||
}
|
||||
|
||||
Ok(response.success)
|
||||
}
|
||||
|
||||
async fn close(&self) {}
|
||||
|
||||
async fn is_online(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
async fn is_local(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user