support distribute lock

Signed-off-by: junxiang Mu <1948535941@qq.com>
This commit is contained in:
junxiang Mu
2024-09-26 11:30:56 +08:00
parent 5e70d8b841
commit ca3dac9a25
41 changed files with 2161 additions and 1275 deletions
+9
View File
@@ -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
+82
View File
@@ -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(())
}
}
+8
View File
@@ -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());
}
+2
View File
@@ -0,0 +1,2 @@
pub mod error;
pub mod globals;
+14 -2
View File
@@ -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
+347
View File
@@ -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
View File
@@ -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(())
+15 -1
View File
@@ -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
)
}
}
+187
View File
@@ -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(())
}
}
+274
View File
@@ -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(())
}
}
+127
View File
@@ -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
+19
View File
@@ -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) {};
}