mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-10 15:16:56 +00:00
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "common"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
lazy_static.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing-error.workspace = true
|
||||
@@ -0,0 +1,82 @@
|
||||
use tracing_error::{SpanTrace, SpanTraceStatus};
|
||||
|
||||
pub type StdError = Box<dyn std::error::Error + Send + Sync + 'static>;
|
||||
|
||||
pub type Result<T = (), E = Error> = std::result::Result<T, E>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
inner: Box<dyn std::error::Error + Send + Sync + 'static>,
|
||||
span_trace: SpanTrace,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Create a new error from a `std::error::Error`.
|
||||
#[must_use]
|
||||
#[track_caller]
|
||||
pub fn new<T: std::error::Error + Send + Sync + 'static>(source: T) -> Self {
|
||||
Self::from_std_error(source.into())
|
||||
}
|
||||
|
||||
/// Create a new error from a `std::error::Error`.
|
||||
#[must_use]
|
||||
#[track_caller]
|
||||
pub fn from_std_error(inner: StdError) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
span_trace: SpanTrace::capture(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new error from a string.
|
||||
#[must_use]
|
||||
#[track_caller]
|
||||
pub fn from_string(s: impl Into<String>) -> Self {
|
||||
Self::msg(s)
|
||||
}
|
||||
|
||||
/// Create a new error from a string.
|
||||
#[must_use]
|
||||
#[track_caller]
|
||||
pub fn msg(s: impl Into<String>) -> Self {
|
||||
Self::from_std_error(s.into().into())
|
||||
}
|
||||
|
||||
/// Returns `true` if the inner type is the same as `T`.
|
||||
#[inline]
|
||||
pub fn is<T: std::error::Error + 'static>(&self) -> bool {
|
||||
self.inner.is::<T>()
|
||||
}
|
||||
|
||||
/// Returns some reference to the inner value if it is of type `T`, or
|
||||
/// `None` if it isn't.
|
||||
#[inline]
|
||||
pub fn downcast_ref<T: std::error::Error + 'static>(&self) -> Option<&T> {
|
||||
self.inner.downcast_ref()
|
||||
}
|
||||
|
||||
/// Returns some mutable reference to the inner value if it is of type `T`, or
|
||||
/// `None` if it isn't.
|
||||
#[inline]
|
||||
pub fn downcast_mut<T: std::error::Error + 'static>(&mut self) -> Option<&mut T> {
|
||||
self.inner.downcast_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: std::error::Error + Send + Sync + 'static> From<T> for Error {
|
||||
fn from(e: T) -> Self {
|
||||
Self::new(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.inner)?;
|
||||
|
||||
if self.span_trace.status() != SpanTraceStatus::EMPTY {
|
||||
write!(f, "\nspan_trace:\n{}", self.span_trace)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
use lazy_static::lazy_static;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref GLOBAL_Local_Node_Name: RwLock<String> = RwLock::new("".to_string());
|
||||
pub static ref GLOBAL_Rustfs_Host: RwLock<String> = RwLock::new("".to_string());
|
||||
pub static ref GLOBAL_Rustfs_Port: RwLock<String> = RwLock::new("9000".to_string());
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod error;
|
||||
pub mod globals;
|
||||
+14
-2
@@ -4,5 +4,17 @@ version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
ecstore.workspace = true
|
||||
tracing.workspace = true
|
||||
async-trait.workspace = true
|
||||
backon.workspace = true
|
||||
common.workspace = true
|
||||
lazy_static.workspace = true
|
||||
protos.workspace = true
|
||||
rand.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tokio.workspace = true
|
||||
tonic.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-error.workspace = true
|
||||
url.workspace = true
|
||||
uuid.workspace = true
|
||||
@@ -0,0 +1,347 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use tokio::{sync::mpsc::Sender, time::sleep};
|
||||
use tracing::{info, warn};
|
||||
|
||||
use crate::{lock_args::LockArgs, LockApi, Locker};
|
||||
|
||||
const DRW_MUTEX_REFRESH_INTERVAL: Duration = Duration::from_secs(10);
|
||||
const LOCK_RETRY_MIN_INTERVAL: Duration = Duration::from_millis(250);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DRWMutex {
|
||||
owner: String,
|
||||
names: Vec<String>,
|
||||
write_locks: Vec<String>,
|
||||
read_locks: Vec<String>,
|
||||
cancel_refresh_sender: Option<Sender<bool>>,
|
||||
// rng: ThreadRng,
|
||||
lockers: Vec<LockApi>,
|
||||
refresh_interval: Duration,
|
||||
lock_retry_min_interval: Duration,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Granted {
|
||||
index: usize,
|
||||
lock_uid: String,
|
||||
}
|
||||
|
||||
impl Granted {
|
||||
fn is_locked(&self) -> bool {
|
||||
is_locked(&self.lock_uid)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_locked(uid: &String) -> bool {
|
||||
uid.len() > 0
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Options {
|
||||
pub timeout: Duration,
|
||||
pub retry_interval: Duration,
|
||||
}
|
||||
|
||||
impl DRWMutex {
|
||||
pub fn new(owner: String, names: Vec<String>, lockers: Vec<LockApi>) -> Self {
|
||||
let mut names = names;
|
||||
names.sort();
|
||||
Self {
|
||||
owner,
|
||||
names,
|
||||
write_locks: Vec::with_capacity(lockers.len()),
|
||||
read_locks: Vec::with_capacity(lockers.len()),
|
||||
cancel_refresh_sender: None,
|
||||
// rng: rand::thread_rng(),
|
||||
lockers,
|
||||
refresh_interval: DRW_MUTEX_REFRESH_INTERVAL,
|
||||
lock_retry_min_interval: LOCK_RETRY_MIN_INTERVAL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DRWMutex {
|
||||
pub async fn lock(&mut self, id: &String, source: &String) {
|
||||
let is_read_lock = false;
|
||||
let opts = Options {
|
||||
timeout: Duration::from_secs(10),
|
||||
retry_interval: Duration::from_millis(50),
|
||||
};
|
||||
self.lock_blocking(id, source, is_read_lock, &opts).await;
|
||||
}
|
||||
|
||||
pub async fn get_lock(&mut self, id: &String, source: &String, opts: &Options) -> bool {
|
||||
let is_read_lock = false;
|
||||
self.lock_blocking(id, source, is_read_lock, opts).await
|
||||
}
|
||||
|
||||
pub async fn r_lock(&mut self, id: &String, source: &String) {
|
||||
let is_read_lock = true;
|
||||
let opts = Options {
|
||||
timeout: Duration::from_secs(10),
|
||||
retry_interval: Duration::from_millis(50),
|
||||
};
|
||||
self.lock_blocking(id, source, is_read_lock, &opts).await;
|
||||
}
|
||||
|
||||
pub async fn get_r_lock(&mut self, id: &String, source: &String, opts: &Options) -> bool {
|
||||
let is_read_lock = true;
|
||||
self.lock_blocking(id, source, is_read_lock, opts).await
|
||||
}
|
||||
|
||||
pub async fn lock_blocking(&mut self, id: &String, source: &String, is_read_lock: bool, opts: &Options) -> bool {
|
||||
let locker_len = self.lockers.len();
|
||||
let mut tolerance = locker_len / 2;
|
||||
let mut quorum = locker_len - tolerance;
|
||||
if !is_read_lock {
|
||||
// In situations for write locks, as a special case
|
||||
// to avoid split brains we make sure to acquire
|
||||
// quorum + 1 when tolerance is exactly half of the
|
||||
// total locker clients.
|
||||
if quorum == tolerance {
|
||||
quorum += 1;
|
||||
}
|
||||
}
|
||||
info!("lockBlocking {}/{} for {:?}: lockType readLock({}), additional opts: {:?}, quorum: {}, tolerance: {}, lockClients: {}\n", id, source, self.names, is_read_lock, opts, quorum, tolerance, locker_len);
|
||||
|
||||
tolerance = locker_len - quorum;
|
||||
let mut attempt = 0;
|
||||
let mut locks = Vec::with_capacity(self.lockers.len());
|
||||
|
||||
loop {
|
||||
if self.inner_lock(&mut locks, id, source, is_read_lock, tolerance, quorum).await {
|
||||
if is_read_lock {
|
||||
self.read_locks = locks;
|
||||
} else {
|
||||
self.write_locks = locks;
|
||||
}
|
||||
|
||||
info!("lock_blocking {}/{} for {:?}: granted", id, source, self.names);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
attempt += 1;
|
||||
if attempt >= 10 {
|
||||
break;
|
||||
}
|
||||
sleep(opts.retry_interval).await;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
async fn inner_lock(
|
||||
&mut self,
|
||||
locks: &mut Vec<String>,
|
||||
id: &String,
|
||||
source: &String,
|
||||
is_read_lock: bool,
|
||||
tolerance: usize,
|
||||
quorum: usize,
|
||||
) -> bool {
|
||||
locks.iter_mut().for_each(|lock| *lock = "".to_string());
|
||||
|
||||
let mut granteds = Vec::with_capacity(self.lockers.len());
|
||||
let args = LockArgs {
|
||||
uid: id.to_string(),
|
||||
resources: self.names.clone(),
|
||||
owner: self.owner.clone(),
|
||||
source: source.to_string(),
|
||||
quorum,
|
||||
};
|
||||
|
||||
for (index, locker) in self.lockers.iter_mut().enumerate() {
|
||||
let mut granted = Granted {
|
||||
index,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if is_read_lock {
|
||||
match locker.rlock(&args).await {
|
||||
Ok(locked) => {
|
||||
if locked {
|
||||
granted.lock_uid = id.to_string();
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Unable to call RLock failed with {} for {} at {:?}", err, args, locker);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match locker.lock(&args).await {
|
||||
Ok(locked) => {
|
||||
if locked {
|
||||
granted.lock_uid = id.to_string();
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Unable to call Lock failed with {} for {} at {:?}", err, args, locker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
granteds.push(granted);
|
||||
}
|
||||
|
||||
granteds.iter().for_each(|granted| {
|
||||
locks[granted.index] = granted.lock_uid.clone();
|
||||
});
|
||||
|
||||
let quorum_locked = check_quorum_locked(locks, quorum);
|
||||
if !quorum_locked {
|
||||
info!("Unable to acquire lock in quorum, {}", args);
|
||||
if !self.release_all(tolerance, locks, is_read_lock).await {
|
||||
info!("Unable to release acquired locks, these locks will expire automatically {}", args);
|
||||
}
|
||||
}
|
||||
|
||||
quorum_locked
|
||||
}
|
||||
|
||||
pub async fn un_lock(&mut self) {
|
||||
if self.write_locks.is_empty() || !self.write_locks.iter().any(|w_lock| is_locked(w_lock)) {
|
||||
panic!("Trying to un_lock() while no lock() is active")
|
||||
}
|
||||
|
||||
let tolerance = self.lockers.len() / 2;
|
||||
let is_read_lock = false;
|
||||
let mut locks = self.write_locks.clone();
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
if self.release_all(tolerance, &mut locks, is_read_lock).await {
|
||||
return;
|
||||
}
|
||||
|
||||
sleep(self.lock_retry_min_interval).await;
|
||||
if Instant::now().duration_since(start) > Duration::from_secs(30) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn un_r_lock(&mut self) {
|
||||
if self.write_locks.is_empty() || !self.write_locks.iter().any(|w_lock| is_locked(w_lock)) {
|
||||
panic!("Trying to un_r_lock() while no r_lock() is active")
|
||||
}
|
||||
|
||||
let tolerance = self.lockers.len() / 2;
|
||||
let is_read_lock = true;
|
||||
let mut locks = self.write_locks.clone();
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
if self.release_all(tolerance, &mut locks, is_read_lock).await {
|
||||
return;
|
||||
}
|
||||
|
||||
sleep(self.lock_retry_min_interval).await;
|
||||
if Instant::now().duration_since(start) > Duration::from_secs(30) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn release_all(&mut self, tolerance: usize, locks: &mut Vec<String>, is_read_lock: bool) -> bool {
|
||||
for (index, locker) in self.lockers.iter_mut().enumerate() {
|
||||
if send_release(locker, &locks[index], &self.owner, &self.names, is_read_lock).await {
|
||||
locks[index] = "".to_string();
|
||||
}
|
||||
}
|
||||
|
||||
check_failed_unlocks(&locks, tolerance)
|
||||
}
|
||||
}
|
||||
|
||||
// async fn start_continuous_lock_refresh(lockers: &Vec<&mut LockApi>, id: &String, source: &String, quorum: usize, refresh_interval: Duration, mut cancel_refresh_receiver: Receiver<bool>) {
|
||||
// let uid = id.to_string();
|
||||
// tokio::spawn(async move {
|
||||
// let mut ticker = interval(refresh_interval);
|
||||
// let args = LockArgs {
|
||||
// uid,
|
||||
// ..Default::default()
|
||||
// };
|
||||
|
||||
// loop {
|
||||
// select! {
|
||||
// _ = ticker.tick() => {
|
||||
// for (index, locker) in lockers.iter().enumerate() {
|
||||
|
||||
// }
|
||||
// },
|
||||
// _ = cancel_refresh_receiver.recv() => {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
fn check_failed_unlocks(locks: &Vec<String>, tolerance: usize) -> bool {
|
||||
let mut un_locks_failed = 0;
|
||||
locks.iter().for_each(|lock| {
|
||||
if is_locked(lock) {
|
||||
un_locks_failed += 1;
|
||||
}
|
||||
});
|
||||
|
||||
if locks.len() - tolerance == tolerance {
|
||||
return un_locks_failed >= tolerance;
|
||||
}
|
||||
|
||||
un_locks_failed > tolerance
|
||||
}
|
||||
|
||||
async fn send_release(locker: &mut LockApi, uid: &String, owner: &String, names: &Vec<String>, is_read_lock: bool) -> bool {
|
||||
if uid.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let args = LockArgs {
|
||||
uid: uid.to_string(),
|
||||
owner: owner.clone(),
|
||||
resources: names.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if is_read_lock {
|
||||
match locker.runlock(&args).await {
|
||||
Ok(locked) => {
|
||||
if !locked {
|
||||
warn!("Unable to release runlock, args: {}", args);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Unable to call RLock failed with {} for {} at {:?}", err, args, locker);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match locker.unlock(&args).await {
|
||||
Ok(locked) => {
|
||||
if !locked {
|
||||
warn!("Unable to release unlock, args: {}", args);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Unable to call Lock failed with {} for {} at {:?}", err, args, locker);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn check_quorum_locked(locks: &Vec<String>, quorum: usize) -> bool {
|
||||
let mut count = 0;
|
||||
locks.iter().for_each(|lock| {
|
||||
if is_locked(lock) {
|
||||
count += 1;
|
||||
}
|
||||
});
|
||||
|
||||
count >= quorum
|
||||
}
|
||||
+114
-1
@@ -1,2 +1,115 @@
|
||||
pub mod local_disk;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use common::error::Result;
|
||||
use lazy_static::lazy_static;
|
||||
use local_locker::LocalLocker;
|
||||
use lock_args::LockArgs;
|
||||
use remote_client::RemoteClinet;
|
||||
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<Box<RwLock<LocalLocker>>> = Arc::new(Box::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(RemoteClinet),
|
||||
}
|
||||
|
||||
#[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(RemoteClinet::new(url.unwrap()))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
use ecstore::error::{Error, Result};
|
||||
use std::{collections::HashMap, time::{Duration, Instant}};
|
||||
use async_trait::async_trait;
|
||||
use common::error::{Error, Result};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::lock_args::LockArgs;
|
||||
use crate::{lock_args::LockArgs, Locker};
|
||||
|
||||
const MAX_DELETE_LIST: usize = 1000;
|
||||
|
||||
@@ -54,7 +58,7 @@ pub struct LocalLocker {
|
||||
}
|
||||
|
||||
impl LocalLocker {
|
||||
fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
LocalLocker::default()
|
||||
}
|
||||
}
|
||||
@@ -64,7 +68,55 @@ impl LocalLocker {
|
||||
resource.iter().fold(true, |acc, x| !self.lock_map.contains_key(x) && acc)
|
||||
}
|
||||
|
||||
pub fn lock(&mut self, args: LockArgs) -> Result<bool> {
|
||||
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.len() > 0 {
|
||||
if value[0].writer {
|
||||
st.writes += 1;
|
||||
} else {
|
||||
st.reads += 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return 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());
|
||||
});
|
||||
|
||||
return 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
|
||||
});
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#[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::from_string(format!(
|
||||
"internal error: LocalLocker.lock called with more than {} resources",
|
||||
@@ -100,7 +152,7 @@ impl LocalLocker {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn unlock(&mut self, args: LockArgs) -> Result<bool> {
|
||||
async fn unlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
if args.resources.len() > MAX_DELETE_LIST {
|
||||
return Err(Error::from_string(format!(
|
||||
"internal error: LocalLocker.unlock called with more than {} resources",
|
||||
@@ -128,24 +180,24 @@ impl LocalLocker {
|
||||
reply |= true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
true
|
||||
});
|
||||
}
|
||||
if lris.len() == 0 {
|
||||
self.lock_map.remove(resource);
|
||||
}
|
||||
},
|
||||
}
|
||||
None => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
pub fn rlock(&mut self, args: LockArgs) -> Result<bool> {
|
||||
async fn rlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
if args.resources.len() != 1 {
|
||||
return Err(Error::from_string("internal error: localLocker.RLock called with more than one resource"));
|
||||
}
|
||||
@@ -166,17 +218,20 @@ impl LocalLocker {
|
||||
} 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()
|
||||
}]);
|
||||
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();
|
||||
@@ -186,7 +241,7 @@ impl LocalLocker {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn runlock(&mut self, args: LockArgs) -> Result<bool> {
|
||||
async fn runlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
if args.resources.len() != 1 {
|
||||
return Err(Error::from_string("internal error: localLocker.RLock called with more than one resource"));
|
||||
}
|
||||
@@ -206,14 +261,14 @@ impl LocalLocker {
|
||||
reply |= true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
true
|
||||
});
|
||||
}
|
||||
if lris.len() == 0 {
|
||||
self.lock_map.remove(resource);
|
||||
}
|
||||
},
|
||||
}
|
||||
None => {
|
||||
return Ok(reply || true);
|
||||
}
|
||||
@@ -222,64 +277,32 @@ impl LocalLocker {
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
pub fn stats(&self) -> LockStats {
|
||||
let mut st = LockStats {
|
||||
total: self.lock_map.len(),
|
||||
..Default::default()
|
||||
};
|
||||
async fn close(&self) {}
|
||||
|
||||
self.lock_map.iter().for_each(|(_, value)| {
|
||||
if value.len() > 0 {
|
||||
if value[0].writer {
|
||||
st.writes += 1;
|
||||
} else {
|
||||
st.reads += 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
pub 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());
|
||||
});
|
||||
|
||||
return lock_copy;
|
||||
}
|
||||
|
||||
pub fn close(&self) {
|
||||
|
||||
}
|
||||
|
||||
pub fn is_online(&self) ->bool {
|
||||
async fn is_online(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn is_local(&self) -> bool {
|
||||
async fn is_local(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// TODO: need add timeout mechanism
|
||||
pub fn force_unlock(&mut self, args: LockArgs) -> Result<bool> {
|
||||
async fn force_unlock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
let mut reply = false;
|
||||
if args.uid.is_empty() {
|
||||
args.resources.iter().for_each(|resource| {
|
||||
match self.lock_map.get(resource) {
|
||||
Some(lris) => {
|
||||
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.len() == 0 {
|
||||
self.lock_map.remove(resource);
|
||||
}
|
||||
},
|
||||
None => (),
|
||||
args.resources.iter().for_each(|resource| match self.lock_map.get(resource) {
|
||||
Some(lris) => {
|
||||
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.len() == 0 {
|
||||
self.lock_map.remove(resource);
|
||||
}
|
||||
}
|
||||
None => (),
|
||||
});
|
||||
|
||||
return Ok(true);
|
||||
@@ -291,32 +314,30 @@ impl LocalLocker {
|
||||
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) => {
|
||||
reply = true;
|
||||
{
|
||||
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.len() == 0 {
|
||||
need_remove_resource.push(resource.to_string());
|
||||
}
|
||||
},
|
||||
None => {
|
||||
need_remove_map_id.push(map_id);
|
||||
idx += 1;
|
||||
continue;
|
||||
Some(resource) => match self.lock_map.get_mut(resource) {
|
||||
Some(lris) => {
|
||||
reply = true;
|
||||
{
|
||||
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.len() == 0 {
|
||||
need_remove_resource.push(resource.to_string());
|
||||
}
|
||||
}
|
||||
None => {
|
||||
need_remove_map_id.push(map_id);
|
||||
idx += 1;
|
||||
continue;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
@@ -335,7 +356,7 @@ impl LocalLocker {
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
pub fn refresh(&mut self, args: LockArgs) -> Result<bool> {
|
||||
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);
|
||||
@@ -344,9 +365,7 @@ impl LocalLocker {
|
||||
let mut resource = resource;
|
||||
loop {
|
||||
match self.lock_map.get_mut(resource) {
|
||||
Some(lris) => {
|
||||
|
||||
},
|
||||
Some(_lris) => {}
|
||||
None => {
|
||||
let mut key = args.uid.to_string();
|
||||
format_uuid(&mut key, &0);
|
||||
@@ -363,29 +382,12 @@ impl LocalLocker {
|
||||
None => return Ok(true),
|
||||
};
|
||||
}
|
||||
},
|
||||
}
|
||||
None => {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
});
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fn format_uuid(s: &mut String, idx: &usize) {
|
||||
@@ -394,12 +396,13 @@ fn format_uuid(s: &mut String, idx: &usize) {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::lock_args::LockArgs;
|
||||
use ecstore::error::Result;
|
||||
use super::LocalLocker;
|
||||
use crate::{lock_args::LockArgs, Locker};
|
||||
use common::error::Result;
|
||||
use tokio;
|
||||
|
||||
#[test]
|
||||
fn test_lock_unlock() -> Result<()> {
|
||||
#[tokio::test]
|
||||
async fn test_lock_unlock() -> Result<()> {
|
||||
let mut local_locker = LocalLocker::new();
|
||||
let args = LockArgs {
|
||||
uid: "1111".to_string(),
|
||||
@@ -408,11 +411,11 @@ mod test {
|
||||
source: "".to_string(),
|
||||
quorum: 3,
|
||||
};
|
||||
local_locker.lock(args.clone())?;
|
||||
local_locker.lock(&args).await?;
|
||||
|
||||
println!("lock local_locker: {:?} \n", local_locker);
|
||||
|
||||
local_locker.unlock(args)?;
|
||||
local_locker.unlock(&args).await?;
|
||||
println!("unlock local_locker: {:?}", local_locker);
|
||||
|
||||
Ok(())
|
||||
@@ -1,4 +1,8 @@
|
||||
#[derive(Clone, Debug, Default)]
|
||||
use std::fmt::Display;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct LockArgs {
|
||||
pub uid: String,
|
||||
pub resources: Vec<String>,
|
||||
@@ -6,3 +10,13 @@ pub struct LockArgs {
|
||||
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,187 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use rand::Rng;
|
||||
use tokio::{sync::RwLock, time::sleep};
|
||||
|
||||
#[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);
|
||||
let x = self
|
||||
.look_loop(
|
||||
&id, &source, &timeout, // big enough
|
||||
is_write,
|
||||
)
|
||||
.await;
|
||||
x
|
||||
}
|
||||
|
||||
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);
|
||||
let x = self
|
||||
.look_loop(
|
||||
&id, &source, &timeout, // big enough
|
||||
is_write,
|
||||
)
|
||||
.await;
|
||||
x
|
||||
}
|
||||
|
||||
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::thread_rng();
|
||||
sleep_time = rng.gen_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 {
|
||||
panic!("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 {
|
||||
panic!("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 {
|
||||
if *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 common::error::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_eq!(true, l_rw_lock.get_lock(id, source, &timeout).await);
|
||||
l_rw_lock.un_lock().await;
|
||||
|
||||
l_rw_lock.lock().await;
|
||||
|
||||
assert_eq!(false, l_rw_lock.get_r_lock(id, source, &timeout).await);
|
||||
l_rw_lock.un_lock().await;
|
||||
assert_eq!(true, 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_eq!(true, 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_eq!(false, two.get_r_lock(id, source, &timeout).await);
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
assert_eq!(true, two.get_r_lock(id, source, &timeout).await);
|
||||
two.un_r_lock().await;
|
||||
};
|
||||
|
||||
tokio::join!(one_fn, two_fn);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
use std::{collections::HashMap, path::Path, sync::Arc, time::Duration};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
drwmutex::{DRWMutex, Options},
|
||||
lrwmutex::LRWMutex,
|
||||
LockApi,
|
||||
};
|
||||
use common::error::Result;
|
||||
|
||||
pub type RWLockerImpl = Box<dyn RWLocker + Send>;
|
||||
|
||||
#[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 {
|
||||
async fn lock(
|
||||
&mut self,
|
||||
volume: &String,
|
||||
path: &String,
|
||||
lock_source: &String,
|
||||
ops_id: &String,
|
||||
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: bool;
|
||||
if read_lock {
|
||||
locked = nslk.lock.get_r_lock(ops_id, lock_source, &timeout).await;
|
||||
} else {
|
||||
locked = nslk.lock.get_lock(ops_id, lock_source, &timeout).await;
|
||||
}
|
||||
|
||||
if !locked {
|
||||
nslk.reference -= 1;
|
||||
if nslk.reference == 0 {
|
||||
w_lock_map.remove(&resource);
|
||||
}
|
||||
}
|
||||
|
||||
return 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 -= 0;
|
||||
|
||||
if nslk.reference == 0 {
|
||||
w_lock_map.remove(&resource);
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn new_nslock(
|
||||
ns: Arc<RwLock<NsLockMap>>,
|
||||
owner: String,
|
||||
volume: String,
|
||||
paths: Vec<String>,
|
||||
lockers: Vec<LockApi>,
|
||||
) -> RWLockerImpl {
|
||||
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 Box::new(DistLockInstance::new(owner, names, lockers));
|
||||
}
|
||||
|
||||
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<()> {
|
||||
Ok(self.lock.un_lock().await)
|
||||
}
|
||||
|
||||
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<()> {
|
||||
Ok(self.lock.un_r_lock().await)
|
||||
}
|
||||
}
|
||||
|
||||
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 common::error::Result;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::{
|
||||
drwmutex::Options,
|
||||
namespace_lock::{new_nslock, NsLockMap},
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_local_instance() -> Result<()> {
|
||||
let ns_lock_map = Arc::new(RwLock::new(NsLockMap::default()));
|
||||
let mut ns = new_nslock(
|
||||
Arc::clone(&ns_lock_map),
|
||||
"local".to_string(),
|
||||
"test".to_string(),
|
||||
vec!["foo".to_string()],
|
||||
Vec::new(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let result = ns
|
||||
.get_lock(&Options {
|
||||
timeout: Duration::from_secs(5),
|
||||
retry_interval: Duration::from_secs(1),
|
||||
})
|
||||
.await?;
|
||||
|
||||
assert_eq!(result, true);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
use async_trait::async_trait;
|
||||
use common::error::{Error, Result};
|
||||
use protos::proto_gen::node_service::{node_service_client::NodeServiceClient, GenerallyLockRequest};
|
||||
use tonic::Request;
|
||||
use tracing::info;
|
||||
|
||||
use crate::{lock_args::LockArgs, Locker};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RemoteClinet {
|
||||
url: url::Url,
|
||||
}
|
||||
|
||||
impl RemoteClinet {
|
||||
pub fn new(url: url::Url) -> Self {
|
||||
Self { url }
|
||||
}
|
||||
|
||||
async fn get_client_v2(&self) -> Result<NodeServiceClient<tonic::transport::Channel>> {
|
||||
// Ok(NodeServiceClient::connect("http://220.181.1.138:9000").await?)
|
||||
let addr = format!("{}://{}:{}", self.url.scheme(), self.url.host_str().unwrap(), self.url.port().unwrap());
|
||||
Ok(NodeServiceClient::connect(addr).await?)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Locker for RemoteClinet {
|
||||
async fn lock(&mut self, args: &LockArgs) -> Result<bool> {
|
||||
info!("remote lock");
|
||||
let args = serde_json::to_string(args)?;
|
||||
let mut client = self.get_client_v2().await?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client.lock(request).await?.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(Error::from_string(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 = self.get_client_v2().await?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client.un_lock(request).await?.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(Error::from_string(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 = self.get_client_v2().await?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client.r_lock(request).await?.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(Error::from_string(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 = self.get_client_v2().await?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client.r_un_lock(request).await?.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(Error::from_string(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 = self.get_client_v2().await?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client.force_un_lock(request).await?.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(Error::from_string(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 = self.get_client_v2().await?;
|
||||
let request = Request::new(GenerallyLockRequest { args });
|
||||
|
||||
let response = client.refresh(request).await?.into_inner();
|
||||
|
||||
if let Some(error_info) = response.error_info {
|
||||
return Err(Error::from_string(error_info));
|
||||
}
|
||||
|
||||
Ok(response.success)
|
||||
}
|
||||
|
||||
async fn is_local(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
async fn close(&self) {}
|
||||
|
||||
async fn is_online(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
|
||||
// @generated
|
||||
|
||||
use core::mem;
|
||||
use core::cmp::Ordering;
|
||||
use core::mem;
|
||||
|
||||
extern crate flatbuffers;
|
||||
use self::flatbuffers::{EndianScalar, Follow};
|
||||
@@ -12,112 +11,114 @@ use self::flatbuffers::{EndianScalar, Follow};
|
||||
#[allow(unused_imports, dead_code)]
|
||||
pub mod models {
|
||||
|
||||
use core::mem;
|
||||
use core::cmp::Ordering;
|
||||
use core::cmp::Ordering;
|
||||
use core::mem;
|
||||
|
||||
extern crate flatbuffers;
|
||||
use self::flatbuffers::{EndianScalar, Follow};
|
||||
extern crate flatbuffers;
|
||||
use self::flatbuffers::{EndianScalar, Follow};
|
||||
|
||||
pub enum PingBodyOffset {}
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum PingBodyOffset {}
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
|
||||
pub struct PingBody<'a> {
|
||||
pub _tab: flatbuffers::Table<'a>,
|
||||
}
|
||||
|
||||
impl<'a> flatbuffers::Follow<'a> for PingBody<'a> {
|
||||
type Inner = PingBody<'a>;
|
||||
#[inline]
|
||||
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
|
||||
Self { _tab: flatbuffers::Table::new(buf, loc) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PingBody<'a> {
|
||||
pub const VT_PAYLOAD: flatbuffers::VOffsetT = 4;
|
||||
|
||||
pub const fn get_fully_qualified_name() -> &'static str {
|
||||
"models.PingBody"
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
|
||||
PingBody { _tab: table }
|
||||
}
|
||||
#[allow(unused_mut)]
|
||||
pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>(
|
||||
_fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>,
|
||||
args: &'args PingBodyArgs<'args>
|
||||
) -> flatbuffers::WIPOffset<PingBody<'bldr>> {
|
||||
let mut builder = PingBodyBuilder::new(_fbb);
|
||||
if let Some(x) = args.payload { builder.add_payload(x); }
|
||||
builder.finish()
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
pub fn payload(&self) -> Option<flatbuffers::Vector<'a, u8>> {
|
||||
// Safety:
|
||||
// Created from valid Table for this object
|
||||
// which contains a valid value in this slot
|
||||
unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, u8>>>(PingBody::VT_PAYLOAD, None)}
|
||||
}
|
||||
}
|
||||
|
||||
impl flatbuffers::Verifiable for PingBody<'_> {
|
||||
#[inline]
|
||||
fn run_verifier(
|
||||
v: &mut flatbuffers::Verifier, pos: usize
|
||||
) -> Result<(), flatbuffers::InvalidFlatbuffer> {
|
||||
use self::flatbuffers::Verifiable;
|
||||
v.visit_table(pos)?
|
||||
.visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, u8>>>("payload", Self::VT_PAYLOAD, false)?
|
||||
.finish();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
pub struct PingBodyArgs<'a> {
|
||||
pub payload: Option<flatbuffers::WIPOffset<flatbuffers::Vector<'a, u8>>>,
|
||||
}
|
||||
impl<'a> Default for PingBodyArgs<'a> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
PingBodyArgs {
|
||||
payload: None,
|
||||
pub struct PingBody<'a> {
|
||||
pub _tab: flatbuffers::Table<'a>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PingBodyBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> {
|
||||
fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>,
|
||||
start_: flatbuffers::WIPOffset<flatbuffers::TableUnfinishedWIPOffset>,
|
||||
}
|
||||
impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> PingBodyBuilder<'a, 'b, A> {
|
||||
#[inline]
|
||||
pub fn add_payload(&mut self, payload: flatbuffers::WIPOffset<flatbuffers::Vector<'b , u8>>) {
|
||||
self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(PingBody::VT_PAYLOAD, payload);
|
||||
}
|
||||
#[inline]
|
||||
pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> PingBodyBuilder<'a, 'b, A> {
|
||||
let start = _fbb.start_table();
|
||||
PingBodyBuilder {
|
||||
fbb_: _fbb,
|
||||
start_: start,
|
||||
impl<'a> flatbuffers::Follow<'a> for PingBody<'a> {
|
||||
type Inner = PingBody<'a>;
|
||||
#[inline]
|
||||
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
|
||||
Self {
|
||||
_tab: flatbuffers::Table::new(buf, loc),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn finish(self) -> flatbuffers::WIPOffset<PingBody<'a>> {
|
||||
let o = self.fbb_.end_table(self.start_);
|
||||
flatbuffers::WIPOffset::new(o.value())
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for PingBody<'_> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let mut ds = f.debug_struct("PingBody");
|
||||
ds.field("payload", &self.payload());
|
||||
ds.finish()
|
||||
}
|
||||
}
|
||||
} // pub mod models
|
||||
impl<'a> PingBody<'a> {
|
||||
pub const VT_PAYLOAD: flatbuffers::VOffsetT = 4;
|
||||
|
||||
pub const fn get_fully_qualified_name() -> &'static str {
|
||||
"models.PingBody"
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
|
||||
PingBody { _tab: table }
|
||||
}
|
||||
#[allow(unused_mut)]
|
||||
pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>(
|
||||
_fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>,
|
||||
args: &'args PingBodyArgs<'args>,
|
||||
) -> flatbuffers::WIPOffset<PingBody<'bldr>> {
|
||||
let mut builder = PingBodyBuilder::new(_fbb);
|
||||
if let Some(x) = args.payload {
|
||||
builder.add_payload(x);
|
||||
}
|
||||
builder.finish()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn payload(&self) -> Option<flatbuffers::Vector<'a, u8>> {
|
||||
// Safety:
|
||||
// Created from valid Table for this object
|
||||
// which contains a valid value in this slot
|
||||
unsafe {
|
||||
self._tab
|
||||
.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, u8>>>(PingBody::VT_PAYLOAD, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl flatbuffers::Verifiable for PingBody<'_> {
|
||||
#[inline]
|
||||
fn run_verifier(v: &mut flatbuffers::Verifier, pos: usize) -> Result<(), flatbuffers::InvalidFlatbuffer> {
|
||||
use self::flatbuffers::Verifiable;
|
||||
v.visit_table(pos)?
|
||||
.visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, u8>>>("payload", Self::VT_PAYLOAD, false)?
|
||||
.finish();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
pub struct PingBodyArgs<'a> {
|
||||
pub payload: Option<flatbuffers::WIPOffset<flatbuffers::Vector<'a, u8>>>,
|
||||
}
|
||||
impl<'a> Default for PingBodyArgs<'a> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
PingBodyArgs { payload: None }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PingBodyBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> {
|
||||
fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>,
|
||||
start_: flatbuffers::WIPOffset<flatbuffers::TableUnfinishedWIPOffset>,
|
||||
}
|
||||
impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> PingBodyBuilder<'a, 'b, A> {
|
||||
#[inline]
|
||||
pub fn add_payload(&mut self, payload: flatbuffers::WIPOffset<flatbuffers::Vector<'b, u8>>) {
|
||||
self.fbb_
|
||||
.push_slot_always::<flatbuffers::WIPOffset<_>>(PingBody::VT_PAYLOAD, payload);
|
||||
}
|
||||
#[inline]
|
||||
pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> PingBodyBuilder<'a, 'b, A> {
|
||||
let start = _fbb.start_table();
|
||||
PingBodyBuilder {
|
||||
fbb_: _fbb,
|
||||
start_: start,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn finish(self) -> flatbuffers::WIPOffset<PingBody<'a>> {
|
||||
let o = self.fbb_.end_table(self.start_);
|
||||
flatbuffers::WIPOffset::new(o.value())
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for PingBody<'_> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let mut ds = f.debug_struct("PingBody");
|
||||
ds.field("payload", &self.payload());
|
||||
ds.finish()
|
||||
}
|
||||
}
|
||||
} // pub mod models
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -292,6 +292,16 @@ message DeleteVolumeResponse {
|
||||
optional string error_info = 2;
|
||||
}
|
||||
|
||||
// lock api have same argument type
|
||||
message GenerallyLockRequest {
|
||||
string args = 1;
|
||||
}
|
||||
|
||||
message GenerallyLockResponse {
|
||||
bool success = 1;
|
||||
optional string error_info = 2;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
service NodeService {
|
||||
@@ -325,4 +335,13 @@ service NodeService {
|
||||
rpc DeleteVersions(DeleteVersionsRequest) returns (DeleteVersionsResponse) {};
|
||||
rpc ReadMultiple(ReadMultipleRequest) returns (ReadMultipleResponse) {};
|
||||
rpc DeleteVolume(DeleteVolumeRequest) returns (DeleteVolumeResponse) {};
|
||||
|
||||
/* -------------------------------lock service-------------------------- */
|
||||
|
||||
rpc Lock(GenerallyLockRequest) returns (GenerallyLockResponse) {};
|
||||
rpc UnLock(GenerallyLockRequest) returns (GenerallyLockResponse) {};
|
||||
rpc RLock(GenerallyLockRequest) returns (GenerallyLockResponse) {};
|
||||
rpc RUnLock(GenerallyLockRequest) returns (GenerallyLockResponse) {};
|
||||
rpc ForceUnLock(GenerallyLockRequest) returns (GenerallyLockResponse) {};
|
||||
rpc Refresh(GenerallyLockRequest) returns (GenerallyLockResponse) {};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user