mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-11 15:46:53 +00:00
refactor: Restructure project layout and clean up dependencies (#30)
This commit introduces a significant reorganization of the project structure to improve maintainability and clarity. Key changes include: - Adjusted the directory layout for a more logical module organization. - Removed unused crate dependencies, reducing the overall project size and potentially speeding up build times. - Updated import paths and configuration files to reflect the structural changes.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,129 @@
|
||||
#![allow(dead_code)]
|
||||
// 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 lazy_static::lazy_static;
|
||||
use local_locker::LocalLocker;
|
||||
use lock_args::LockArgs;
|
||||
use remote_client::RemoteClient;
|
||||
use std::io::Result;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub mod drwmutex;
|
||||
pub mod local_locker;
|
||||
pub mod lock_args;
|
||||
pub mod lrwmutex;
|
||||
pub mod namespace_lock;
|
||||
pub mod remote_client;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref GLOBAL_LOCAL_SERVER: Arc<RwLock<LocalLocker>> = Arc::new(RwLock::new(LocalLocker::new()));
|
||||
}
|
||||
|
||||
type LockClient = dyn Locker;
|
||||
|
||||
#[async_trait]
|
||||
pub trait Locker {
|
||||
async fn lock(&mut self, args: &LockArgs) -> Result<bool>;
|
||||
async fn unlock(&mut self, args: &LockArgs) -> Result<bool>;
|
||||
async fn rlock(&mut self, args: &LockArgs) -> Result<bool>;
|
||||
async fn runlock(&mut self, args: &LockArgs) -> Result<bool>;
|
||||
async fn refresh(&mut self, args: &LockArgs) -> Result<bool>;
|
||||
async fn force_unlock(&mut self, args: &LockArgs) -> Result<bool>;
|
||||
async fn close(&self);
|
||||
async fn is_online(&self) -> bool;
|
||||
async fn is_local(&self) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum LockApi {
|
||||
Local,
|
||||
Remote(RemoteClient),
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Locker for LockApi {
|
||||
async fn lock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
match self {
|
||||
LockApi::Local => GLOBAL_LOCAL_SERVER.write().await.lock(args).await,
|
||||
LockApi::Remote(r) => r.lock(args).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn unlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
match self {
|
||||
LockApi::Local => GLOBAL_LOCAL_SERVER.write().await.unlock(args).await,
|
||||
LockApi::Remote(r) => r.unlock(args).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn rlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
match self {
|
||||
LockApi::Local => GLOBAL_LOCAL_SERVER.write().await.rlock(args).await,
|
||||
LockApi::Remote(r) => r.rlock(args).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn runlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
match self {
|
||||
LockApi::Local => GLOBAL_LOCAL_SERVER.write().await.runlock(args).await,
|
||||
LockApi::Remote(r) => r.runlock(args).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn refresh(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
match self {
|
||||
LockApi::Local => GLOBAL_LOCAL_SERVER.write().await.refresh(args).await,
|
||||
LockApi::Remote(r) => r.refresh(args).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn force_unlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
match self {
|
||||
LockApi::Local => GLOBAL_LOCAL_SERVER.write().await.force_unlock(args).await,
|
||||
LockApi::Remote(r) => r.force_unlock(args).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn close(&self) {
|
||||
match self {
|
||||
LockApi::Local => GLOBAL_LOCAL_SERVER.read().await.close().await,
|
||||
LockApi::Remote(r) => r.close().await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn is_online(&self) -> bool {
|
||||
match self {
|
||||
LockApi::Local => GLOBAL_LOCAL_SERVER.read().await.is_online().await,
|
||||
LockApi::Remote(r) => r.is_online().await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn is_local(&self) -> bool {
|
||||
match self {
|
||||
LockApi::Local => GLOBAL_LOCAL_SERVER.write().await.is_local().await,
|
||||
LockApi::Remote(r) => r.is_local().await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_lock_api(is_local: bool, url: Option<url::Url>) -> LockApi {
|
||||
if is_local {
|
||||
return LockApi::Local;
|
||||
}
|
||||
|
||||
LockApi::Remote(RemoteClient::new(url.unwrap()))
|
||||
}
|
||||
@@ -0,0 +1,427 @@
|
||||
// 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 std::io::{Error, Result};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::{Locker, lock_args::LockArgs};
|
||||
|
||||
pub const MAX_DELETE_LIST: usize = 1000;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct LockRequesterInfo {
|
||||
name: String,
|
||||
writer: bool,
|
||||
uid: String,
|
||||
time_stamp: Instant,
|
||||
time_last_refresh: Instant,
|
||||
source: String,
|
||||
group: bool,
|
||||
owner: String,
|
||||
quorum: usize,
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
impl Default for LockRequesterInfo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: Default::default(),
|
||||
writer: Default::default(),
|
||||
uid: Default::default(),
|
||||
time_stamp: Instant::now(),
|
||||
time_last_refresh: Instant::now(),
|
||||
source: Default::default(),
|
||||
group: Default::default(),
|
||||
owner: Default::default(),
|
||||
quorum: Default::default(),
|
||||
idx: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_write_lock(lri: &[LockRequesterInfo]) -> bool {
|
||||
lri.len() == 1 && lri[0].writer
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct LockStats {
|
||||
total: usize,
|
||||
writes: usize,
|
||||
reads: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct LocalLocker {
|
||||
lock_map: HashMap<String, Vec<LockRequesterInfo>>,
|
||||
lock_uid: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl LocalLocker {
|
||||
pub fn new() -> Self {
|
||||
LocalLocker::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalLocker {
|
||||
fn can_take_lock(&self, resource: &[String]) -> bool {
|
||||
resource.iter().fold(true, |acc, x| !self.lock_map.contains_key(x) && acc)
|
||||
}
|
||||
|
||||
pub fn stats(&self) -> LockStats {
|
||||
let mut st = LockStats {
|
||||
total: self.lock_map.len(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
self.lock_map.iter().for_each(|(_, value)| {
|
||||
if !value.is_empty() {
|
||||
if value[0].writer {
|
||||
st.writes += 1;
|
||||
} else {
|
||||
st.reads += 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
st
|
||||
}
|
||||
|
||||
fn dump_lock_map(&mut self) -> HashMap<String, Vec<LockRequesterInfo>> {
|
||||
let mut lock_copy = HashMap::new();
|
||||
self.lock_map.iter().for_each(|(key, value)| {
|
||||
lock_copy.insert(key.to_string(), value.to_vec());
|
||||
});
|
||||
|
||||
lock_copy
|
||||
}
|
||||
|
||||
fn expire_old_locks(&mut self, interval: Duration) {
|
||||
self.lock_map.iter_mut().for_each(|(_, lris)| {
|
||||
lris.retain(|lri| {
|
||||
if Instant::now().duration_since(lri.time_last_refresh) > interval {
|
||||
let mut key = lri.uid.to_string();
|
||||
format_uuid(&mut key, &lri.idx);
|
||||
self.lock_uid.remove(&key);
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Locker for LocalLocker {
|
||||
async fn lock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
if args.resources.len() > MAX_DELETE_LIST {
|
||||
return Err(Error::other(format!(
|
||||
"internal error: LocalLocker.lock called with more than {MAX_DELETE_LIST} resources"
|
||||
)));
|
||||
}
|
||||
|
||||
if !self.can_take_lock(&args.resources) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
args.resources.iter().enumerate().for_each(|(idx, resource)| {
|
||||
self.lock_map.insert(
|
||||
resource.to_string(),
|
||||
vec![LockRequesterInfo {
|
||||
name: resource.to_string(),
|
||||
writer: true,
|
||||
source: args.source.to_string(),
|
||||
owner: args.owner.to_string(),
|
||||
uid: args.uid.to_string(),
|
||||
group: args.resources.len() > 1,
|
||||
quorum: args.quorum,
|
||||
idx,
|
||||
..Default::default()
|
||||
}],
|
||||
);
|
||||
|
||||
let mut uuid = args.uid.to_string();
|
||||
format_uuid(&mut uuid, &idx);
|
||||
self.lock_uid.insert(uuid, resource.to_string());
|
||||
});
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn unlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
if args.resources.len() > MAX_DELETE_LIST {
|
||||
return Err(Error::other(format!(
|
||||
"internal error: LocalLocker.unlock called with more than {MAX_DELETE_LIST} resources"
|
||||
)));
|
||||
}
|
||||
|
||||
let mut reply = false;
|
||||
let mut err_info = String::new();
|
||||
for resource in args.resources.iter() {
|
||||
match self.lock_map.get_mut(resource) {
|
||||
Some(lris) => {
|
||||
if !is_write_lock(lris) {
|
||||
if err_info.is_empty() {
|
||||
err_info = format!("unlock attempted on a read locked entity: {resource}");
|
||||
} else {
|
||||
err_info.push_str(&format!(", {resource}"));
|
||||
}
|
||||
} else {
|
||||
lris.retain(|lri| {
|
||||
if lri.uid == args.uid && (args.owner.is_empty() || lri.owner == args.owner) {
|
||||
let mut key = args.uid.to_string();
|
||||
format_uuid(&mut key, &lri.idx);
|
||||
self.lock_uid.remove(&key).unwrap();
|
||||
reply |= true;
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
}
|
||||
if lris.is_empty() {
|
||||
self.lock_map.remove(resource);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
async fn rlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
if args.resources.len() != 1 {
|
||||
return Err(Error::other("internal error: localLocker.RLock called with more than one resource"));
|
||||
}
|
||||
|
||||
let resource = &args.resources[0];
|
||||
match self.lock_map.get_mut(resource) {
|
||||
Some(lri) => {
|
||||
if !is_write_lock(lri) {
|
||||
lri.push(LockRequesterInfo {
|
||||
name: resource.to_string(),
|
||||
writer: false,
|
||||
source: args.source.to_string(),
|
||||
owner: args.owner.to_string(),
|
||||
uid: args.uid.to_string(),
|
||||
quorum: args.quorum,
|
||||
..Default::default()
|
||||
});
|
||||
} else {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.lock_map.insert(
|
||||
resource.to_string(),
|
||||
vec![LockRequesterInfo {
|
||||
name: resource.to_string(),
|
||||
writer: false,
|
||||
source: args.source.to_string(),
|
||||
owner: args.owner.to_string(),
|
||||
uid: args.uid.to_string(),
|
||||
quorum: args.quorum,
|
||||
..Default::default()
|
||||
}],
|
||||
);
|
||||
}
|
||||
}
|
||||
let mut uuid = args.uid.to_string();
|
||||
format_uuid(&mut uuid, &0);
|
||||
self.lock_uid.insert(uuid, resource.to_string());
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn runlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
if args.resources.len() != 1 {
|
||||
return Err(Error::other("internal error: localLocker.RLock called with more than one resource"));
|
||||
}
|
||||
|
||||
let mut reply = false;
|
||||
let resource = &args.resources[0];
|
||||
match self.lock_map.get_mut(resource) {
|
||||
Some(lris) => {
|
||||
if is_write_lock(lris) {
|
||||
return Err(Error::other(format!("runlock attempted on a write locked entity: {resource}")));
|
||||
} else {
|
||||
lris.retain(|lri| {
|
||||
if lri.uid == args.uid && (args.owner.is_empty() || lri.owner == args.owner) {
|
||||
let mut key = args.uid.to_string();
|
||||
format_uuid(&mut key, &lri.idx);
|
||||
self.lock_uid.remove(&key).unwrap();
|
||||
reply |= true;
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
}
|
||||
if lris.is_empty() {
|
||||
self.lock_map.remove(resource);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Ok(reply);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
async fn refresh(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
let mut idx = 0;
|
||||
let mut key = args.uid.to_string();
|
||||
format_uuid(&mut key, &idx);
|
||||
match self.lock_uid.get(&key) {
|
||||
Some(resource) => {
|
||||
let mut resource = resource;
|
||||
loop {
|
||||
match self.lock_map.get_mut(resource) {
|
||||
Some(_lris) => {}
|
||||
None => {
|
||||
let mut key = args.uid.to_string();
|
||||
format_uuid(&mut key, &0);
|
||||
self.lock_uid.remove(&key);
|
||||
return Ok(idx > 0);
|
||||
}
|
||||
}
|
||||
|
||||
idx += 1;
|
||||
let mut key = args.uid.to_string();
|
||||
format_uuid(&mut key, &idx);
|
||||
resource = match self.lock_uid.get(&key) {
|
||||
Some(resource) => resource,
|
||||
None => return Ok(true),
|
||||
};
|
||||
}
|
||||
}
|
||||
None => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: need add timeout mechanism
|
||||
async fn force_unlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
if args.uid.is_empty() {
|
||||
args.resources.iter().for_each(|resource| {
|
||||
if let Some(lris) = self.lock_map.get(resource) {
|
||||
lris.iter().for_each(|lri| {
|
||||
let mut key = lri.uid.to_string();
|
||||
format_uuid(&mut key, &lri.idx);
|
||||
self.lock_uid.remove(&key);
|
||||
});
|
||||
if lris.is_empty() {
|
||||
self.lock_map.remove(resource);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
let mut idx = 0;
|
||||
let mut need_remove_resource = Vec::new();
|
||||
let mut need_remove_map_id = Vec::new();
|
||||
let reply = loop {
|
||||
let mut map_id = args.uid.to_string();
|
||||
format_uuid(&mut map_id, &idx);
|
||||
match self.lock_uid.get(&map_id) {
|
||||
Some(resource) => match self.lock_map.get_mut(resource) {
|
||||
Some(lris) => {
|
||||
{
|
||||
lris.retain(|lri| {
|
||||
if lri.uid == args.uid && (args.owner.is_empty() || lri.owner == args.owner) {
|
||||
let mut key = args.uid.to_string();
|
||||
format_uuid(&mut key, &lri.idx);
|
||||
need_remove_map_id.push(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
}
|
||||
idx += 1;
|
||||
if lris.is_empty() {
|
||||
need_remove_resource.push(resource.to_string());
|
||||
}
|
||||
}
|
||||
None => {
|
||||
need_remove_map_id.push(map_id);
|
||||
idx += 1;
|
||||
continue;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
break idx > 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
need_remove_resource.into_iter().for_each(|resource| {
|
||||
self.lock_map.remove(&resource);
|
||||
});
|
||||
need_remove_map_id.into_iter().for_each(|map_id| {
|
||||
self.lock_uid.remove(&map_id);
|
||||
});
|
||||
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
async fn close(&self) {}
|
||||
|
||||
async fn is_online(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
async fn is_local(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn format_uuid(s: &mut String, idx: &usize) {
|
||||
s.push_str(&idx.to_string());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::LocalLocker;
|
||||
use crate::{Locker, lock_args::LockArgs};
|
||||
use std::io::Result;
|
||||
use tokio;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_lock_unlock() -> Result<()> {
|
||||
let mut local_locker = LocalLocker::new();
|
||||
let args = LockArgs {
|
||||
uid: "1111".to_string(),
|
||||
resources: vec!["dandan".to_string()],
|
||||
owner: "dd".to_string(),
|
||||
source: "".to_string(),
|
||||
quorum: 3,
|
||||
};
|
||||
local_locker.lock(&args).await?;
|
||||
|
||||
println!("lock local_locker: {local_locker:?} \n");
|
||||
|
||||
local_locker.unlock(&args).await?;
|
||||
println!("unlock local_locker: {local_locker:?}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct LockArgs {
|
||||
pub uid: String,
|
||||
pub resources: Vec<String>,
|
||||
pub owner: String,
|
||||
pub source: String,
|
||||
pub quorum: usize,
|
||||
}
|
||||
|
||||
impl Display for LockArgs {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"LockArgs[ uid: {}, resources: {:?}, owner: {}, source:{}, quorum: {} ]",
|
||||
self.uid, self.resources, self.owner, self.source, self.quorum
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
// 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 rand::Rng;
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio::{sync::RwLock, time::sleep};
|
||||
use tracing::info;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct LRWMutex {
|
||||
id: RwLock<String>,
|
||||
source: RwLock<String>,
|
||||
is_write: RwLock<bool>,
|
||||
refrence: RwLock<usize>,
|
||||
}
|
||||
|
||||
impl LRWMutex {
|
||||
pub async fn lock(&self) -> bool {
|
||||
let is_write = true;
|
||||
let id = self.id.read().await.clone();
|
||||
let source = self.source.read().await.clone();
|
||||
let timeout = Duration::from_secs(10000);
|
||||
self.look_loop(
|
||||
&id, &source, &timeout, // big enough
|
||||
is_write,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_lock(&self, id: &str, source: &str, timeout: &Duration) -> bool {
|
||||
let is_write = true;
|
||||
self.look_loop(id, source, timeout, is_write).await
|
||||
}
|
||||
|
||||
pub async fn r_lock(&self) -> bool {
|
||||
let is_write: bool = false;
|
||||
let id = self.id.read().await.clone();
|
||||
let source = self.source.read().await.clone();
|
||||
let timeout = Duration::from_secs(10000);
|
||||
self.look_loop(
|
||||
&id, &source, &timeout, // big enough
|
||||
is_write,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_r_lock(&self, id: &str, source: &str, timeout: &Duration) -> bool {
|
||||
let is_write = false;
|
||||
self.look_loop(id, source, timeout, is_write).await
|
||||
}
|
||||
|
||||
async fn inner_lock(&self, id: &str, source: &str, is_write: bool) -> bool {
|
||||
*self.id.write().await = id.to_string();
|
||||
*self.source.write().await = source.to_string();
|
||||
|
||||
let mut locked = false;
|
||||
if is_write {
|
||||
if *self.refrence.read().await == 0 && !*self.is_write.read().await {
|
||||
*self.refrence.write().await = 1;
|
||||
*self.is_write.write().await = true;
|
||||
locked = true;
|
||||
}
|
||||
} else if !*self.is_write.read().await {
|
||||
*self.refrence.write().await += 1;
|
||||
locked = true;
|
||||
}
|
||||
|
||||
locked
|
||||
}
|
||||
|
||||
async fn look_loop(&self, id: &str, source: &str, timeout: &Duration, is_write: bool) -> bool {
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
if self.inner_lock(id, source, is_write).await {
|
||||
return true;
|
||||
} else {
|
||||
if Instant::now().duration_since(start) > *timeout {
|
||||
return false;
|
||||
}
|
||||
let sleep_time: u64;
|
||||
{
|
||||
let mut rng = rand::rng();
|
||||
sleep_time = rng.random_range(10..=50);
|
||||
}
|
||||
sleep(Duration::from_millis(sleep_time)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn un_lock(&self) {
|
||||
let is_write = true;
|
||||
if !self.unlock(is_write).await {
|
||||
info!("Trying to un_lock() while no Lock() is active")
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn un_r_lock(&self) {
|
||||
let is_write = false;
|
||||
if !self.unlock(is_write).await {
|
||||
info!("Trying to un_r_lock() while no Lock() is active")
|
||||
}
|
||||
}
|
||||
|
||||
async fn unlock(&self, is_write: bool) -> bool {
|
||||
let mut unlocked = false;
|
||||
if is_write {
|
||||
if *self.is_write.read().await && *self.refrence.read().await == 1 {
|
||||
*self.refrence.write().await = 0;
|
||||
*self.is_write.write().await = false;
|
||||
unlocked = true;
|
||||
}
|
||||
} else if !*self.is_write.read().await && *self.refrence.read().await > 0 {
|
||||
*self.refrence.write().await -= 1;
|
||||
unlocked = true;
|
||||
}
|
||||
|
||||
unlocked
|
||||
}
|
||||
|
||||
pub async fn force_un_lock(&self) {
|
||||
*self.refrence.write().await = 0;
|
||||
*self.is_write.write().await = false;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use std::io::Result;
|
||||
use tokio::time::sleep;
|
||||
|
||||
use crate::lrwmutex::LRWMutex;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_lock_unlock() -> Result<()> {
|
||||
let l_rw_lock = LRWMutex::default();
|
||||
let id = "foo";
|
||||
let source = "dandan";
|
||||
let timeout = Duration::from_secs(5);
|
||||
assert!(l_rw_lock.get_lock(id, source, &timeout).await);
|
||||
l_rw_lock.un_lock().await;
|
||||
|
||||
l_rw_lock.lock().await;
|
||||
|
||||
assert!(!l_rw_lock.get_r_lock(id, source, &timeout).await);
|
||||
l_rw_lock.un_lock().await;
|
||||
assert!(l_rw_lock.get_r_lock(id, source, &timeout).await);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_thread_test() -> Result<()> {
|
||||
let l_rw_lock = Arc::new(LRWMutex::default());
|
||||
let id = "foo";
|
||||
let source = "dandan";
|
||||
|
||||
let one_fn = async {
|
||||
let one = Arc::clone(&l_rw_lock);
|
||||
let timeout = Duration::from_secs(1);
|
||||
assert!(one.get_lock(id, source, &timeout).await);
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
l_rw_lock.un_lock().await;
|
||||
};
|
||||
|
||||
let two_fn = async {
|
||||
let two = Arc::clone(&l_rw_lock);
|
||||
let timeout = Duration::from_secs(2);
|
||||
assert!(!two.get_r_lock(id, source, &timeout).await);
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
assert!(two.get_r_lock(id, source, &timeout).await);
|
||||
two.un_r_lock().await;
|
||||
};
|
||||
|
||||
tokio::join!(one_fn, two_fn);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
// 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 std::{collections::HashMap, path::Path, sync::Arc, time::Duration};
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
LockApi,
|
||||
drwmutex::{DRWMutex, Options},
|
||||
lrwmutex::LRWMutex,
|
||||
};
|
||||
use std::io::Result;
|
||||
|
||||
pub type RWLockerImpl = Box<dyn RWLocker + Send + Sync>;
|
||||
|
||||
#[async_trait]
|
||||
pub trait RWLocker {
|
||||
async fn get_lock(&mut self, opts: &Options) -> Result<bool>;
|
||||
async fn un_lock(&mut self) -> Result<()>;
|
||||
async fn get_u_lock(&mut self, opts: &Options) -> Result<bool>;
|
||||
async fn un_r_lock(&mut self) -> Result<()>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NsLock {
|
||||
reference: usize,
|
||||
lock: LRWMutex,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct NsLockMap {
|
||||
is_dist_erasure: bool,
|
||||
lock_map: RwLock<HashMap<String, NsLock>>,
|
||||
}
|
||||
|
||||
impl NsLockMap {
|
||||
pub fn new(is_dist_erasure: bool) -> Self {
|
||||
Self {
|
||||
is_dist_erasure,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
async fn lock(
|
||||
&mut self,
|
||||
volume: &String,
|
||||
path: &String,
|
||||
lock_source: &str,
|
||||
ops_id: &str,
|
||||
read_lock: bool,
|
||||
timeout: Duration,
|
||||
) -> bool {
|
||||
let resource = Path::new(volume).join(path).to_str().unwrap().to_string();
|
||||
let mut w_lock_map = self.lock_map.write().await;
|
||||
let nslk = w_lock_map.entry(resource.clone()).or_insert(NsLock {
|
||||
reference: 0,
|
||||
lock: LRWMutex::default(),
|
||||
});
|
||||
nslk.reference += 1;
|
||||
|
||||
let locked = if read_lock {
|
||||
nslk.lock.get_r_lock(ops_id, lock_source, &timeout).await
|
||||
} else {
|
||||
nslk.lock.get_lock(ops_id, lock_source, &timeout).await
|
||||
};
|
||||
|
||||
if !locked {
|
||||
nslk.reference -= 1;
|
||||
if nslk.reference == 0 {
|
||||
w_lock_map.remove(&resource);
|
||||
}
|
||||
}
|
||||
|
||||
locked
|
||||
}
|
||||
|
||||
async fn un_lock(&mut self, volume: &String, path: &String, read_lock: bool) {
|
||||
let resource = Path::new(volume).join(path).to_str().unwrap().to_string();
|
||||
let mut w_lock_map = self.lock_map.write().await;
|
||||
if let Some(nslk) = w_lock_map.get_mut(&resource) {
|
||||
if read_lock {
|
||||
nslk.lock.un_r_lock().await;
|
||||
} else {
|
||||
nslk.lock.un_lock().await;
|
||||
}
|
||||
|
||||
nslk.reference -= 1;
|
||||
|
||||
if nslk.reference == 0 {
|
||||
w_lock_map.remove(&resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WrapperLocker(pub Arc<RwLock<RWLockerImpl>>);
|
||||
|
||||
impl Drop for WrapperLocker {
|
||||
fn drop(&mut self) {
|
||||
let inner = self.0.clone();
|
||||
tokio::spawn(async move {
|
||||
let _ = inner.write().await.un_lock().await;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn new_nslock(
|
||||
ns: Arc<RwLock<NsLockMap>>,
|
||||
owner: String,
|
||||
volume: String,
|
||||
paths: Vec<String>,
|
||||
lockers: Vec<LockApi>,
|
||||
) -> WrapperLocker {
|
||||
if ns.read().await.is_dist_erasure {
|
||||
let names = paths
|
||||
.iter()
|
||||
.map(|path| Path::new(&volume).join(path).to_str().unwrap().to_string())
|
||||
.collect();
|
||||
return WrapperLocker(Arc::new(RwLock::new(Box::new(DistLockInstance::new(owner, names, lockers)))));
|
||||
}
|
||||
|
||||
WrapperLocker(Arc::new(RwLock::new(Box::new(LocalLockInstance::new(ns, volume, paths)))))
|
||||
}
|
||||
|
||||
struct DistLockInstance {
|
||||
lock: Box<DRWMutex>,
|
||||
ops_id: String,
|
||||
}
|
||||
|
||||
impl DistLockInstance {
|
||||
fn new(owner: String, names: Vec<String>, lockers: Vec<LockApi>) -> Self {
|
||||
let ops_id = Uuid::new_v4().to_string();
|
||||
Self {
|
||||
lock: Box::new(DRWMutex::new(owner, names, lockers)),
|
||||
ops_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl RWLocker for DistLockInstance {
|
||||
async fn get_lock(&mut self, opts: &Options) -> Result<bool> {
|
||||
let source = "".to_string();
|
||||
|
||||
Ok(self.lock.get_lock(&self.ops_id, &source, opts).await)
|
||||
}
|
||||
|
||||
async fn un_lock(&mut self) -> Result<()> {
|
||||
self.lock.un_lock().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_u_lock(&mut self, opts: &Options) -> Result<bool> {
|
||||
let source = "".to_string();
|
||||
|
||||
Ok(self.lock.get_r_lock(&self.ops_id, &source, opts).await)
|
||||
}
|
||||
|
||||
async fn un_r_lock(&mut self) -> Result<()> {
|
||||
self.lock.un_r_lock().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct LocalLockInstance {
|
||||
ns: Arc<RwLock<NsLockMap>>,
|
||||
volume: String,
|
||||
paths: Vec<String>,
|
||||
ops_id: String,
|
||||
}
|
||||
|
||||
impl LocalLockInstance {
|
||||
fn new(ns: Arc<RwLock<NsLockMap>>, volume: String, paths: Vec<String>) -> Self {
|
||||
let ops_id = Uuid::new_v4().to_string();
|
||||
Self {
|
||||
ns,
|
||||
volume,
|
||||
paths,
|
||||
ops_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl RWLocker for LocalLockInstance {
|
||||
async fn get_lock(&mut self, opts: &Options) -> Result<bool> {
|
||||
let source = "".to_string();
|
||||
let read_lock = false;
|
||||
let mut success = vec![false; self.paths.len()];
|
||||
for (idx, path) in self.paths.iter().enumerate() {
|
||||
if !self
|
||||
.ns
|
||||
.write()
|
||||
.await
|
||||
.lock(&self.volume, path, &source, &self.ops_id, read_lock, opts.timeout)
|
||||
.await
|
||||
{
|
||||
for (i, x) in success.iter().enumerate() {
|
||||
if *x {
|
||||
self.ns.write().await.un_lock(&self.volume, &self.paths[i], read_lock).await;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
success[idx] = true;
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn un_lock(&mut self) -> Result<()> {
|
||||
let read_lock = false;
|
||||
for path in self.paths.iter() {
|
||||
self.ns.write().await.un_lock(&self.volume, path, read_lock).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_u_lock(&mut self, opts: &Options) -> Result<bool> {
|
||||
let source = "".to_string();
|
||||
let read_lock = true;
|
||||
let mut success = Vec::with_capacity(self.paths.len());
|
||||
for (idx, path) in self.paths.iter().enumerate() {
|
||||
if !self
|
||||
.ns
|
||||
.write()
|
||||
.await
|
||||
.lock(&self.volume, path, &source, &self.ops_id, read_lock, opts.timeout)
|
||||
.await
|
||||
{
|
||||
for (i, x) in success.iter().enumerate() {
|
||||
if *x {
|
||||
self.ns.write().await.un_lock(&self.volume, &self.paths[i], read_lock).await;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
success[idx] = true;
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn un_r_lock(&mut self) -> Result<()> {
|
||||
let read_lock = true;
|
||||
for path in self.paths.iter() {
|
||||
self.ns.write().await.un_lock(&self.volume, path, read_lock).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use std::io::Result;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::{
|
||||
drwmutex::Options,
|
||||
namespace_lock::{NsLockMap, new_nslock},
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_local_instance() -> Result<()> {
|
||||
let ns_lock_map = Arc::new(RwLock::new(NsLockMap::default()));
|
||||
let ns = new_nslock(
|
||||
Arc::clone(&ns_lock_map),
|
||||
"local".to_string(),
|
||||
"test".to_string(),
|
||||
vec!["foo".to_string()],
|
||||
Vec::new(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let result =
|
||||
ns.0.write()
|
||||
.await
|
||||
.get_lock(&Options {
|
||||
timeout: Duration::from_secs(5),
|
||||
retry_interval: Duration::from_secs(1),
|
||||
})
|
||||
.await?;
|
||||
|
||||
assert!(result);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
// 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 crate::{Locker, lock_args::LockArgs};
|
||||
use async_trait::async_trait;
|
||||
use rustfs_protos::{node_service_time_out_client, proto_gen::node_service::GenerallyLockRequest};
|
||||
use std::io::{Error, Result};
|
||||
use tonic::Request;
|
||||
use tracing::info;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RemoteClient {
|
||||
addr: String,
|
||||
}
|
||||
|
||||
impl RemoteClient {
|
||||
pub fn new(url: url::Url) -> Self {
|
||||
let addr = format!("{}://{}:{}", url.scheme(), url.host_str().unwrap(), url.port().unwrap());
|
||||
Self { addr }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Locker for RemoteClient {
|
||||
async fn lock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
info!("remote lock");
|
||||
let args = serde_json::to_string(args)?;
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| Error::other(format!("can not get client, err: {err}")))?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client.lock(request).await.map_err(Error::other)?.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(Error::other(error_info));
|
||||
}
|
||||
|
||||
Ok(response.success)
|
||||
}
|
||||
|
||||
async fn unlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
info!("remote unlock");
|
||||
let args = serde_json::to_string(args)?;
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| Error::other(format!("can not get client, err: {err}")))?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client.un_lock(request).await.map_err(Error::other)?.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(Error::other(error_info));
|
||||
}
|
||||
|
||||
Ok(response.success)
|
||||
}
|
||||
|
||||
async fn rlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
info!("remote rlock");
|
||||
let args = serde_json::to_string(args)?;
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| Error::other(format!("can not get client, err: {err}")))?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client.r_lock(request).await.map_err(Error::other)?.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(Error::other(error_info));
|
||||
}
|
||||
|
||||
Ok(response.success)
|
||||
}
|
||||
|
||||
async fn runlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
info!("remote runlock");
|
||||
let args = serde_json::to_string(args)?;
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| Error::other(format!("can not get client, err: {err}")))?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client.r_un_lock(request).await.map_err(Error::other)?.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(Error::other(error_info));
|
||||
}
|
||||
|
||||
Ok(response.success)
|
||||
}
|
||||
|
||||
async fn refresh(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
info!("remote refresh");
|
||||
let args = serde_json::to_string(args)?;
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| Error::other(format!("can not get client, err: {err}")))?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client.refresh(request).await.map_err(Error::other)?.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(Error::other(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)?;
|
||||
let mut client = node_service_time_out_client(&self.addr)
|
||||
.await
|
||||
.map_err(|err| Error::other(format!("can not get client, err: {err}")))?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client.force_un_lock(request).await.map_err(Error::other)?.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(Error::other(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