Signed-off-by: root <root@PC.localdomain>
This commit is contained in:
root
2025-07-06 21:52:28 +08:00
committed by junxiang Mu
parent 4ccdeb9d2a
commit dc44cde081
24 changed files with 3308 additions and 4772 deletions
+1 -1
View File
@@ -22,4 +22,4 @@ pub use http_auth::{build_auth_headers, verify_rpc_signature};
pub use peer_rest_client::PeerRestClient;
pub use peer_s3_client::{LocalPeerS3Client, PeerS3Client, RemotePeerS3Client, S3PeerSys};
pub use remote_disk::RemoteDisk;
pub use tonic_service::make_server;
pub use tonic_service::{make_server, NodeService};
+157 -130
View File
@@ -34,7 +34,6 @@ use crate::{
};
use futures::{Stream, StreamExt};
use futures_util::future::join_all;
use rustfs_lock::{GLOBAL_LOCAL_SERVER, core::local::LocalLockManager, lock_args::LockArgs};
use rustfs_common::globals::GLOBAL_Local_Node_Name;
@@ -81,11 +80,16 @@ type ResponseStream<T> = Pin<Box<dyn Stream<Item = Result<T, tonic::Status>> + S
#[derive(Debug)]
pub struct NodeService {
local_peer: LocalPeerS3Client,
lock_manager: rustfs_lock::NsLockMap,
}
pub fn make_server() -> NodeService {
let local_peer = LocalPeerS3Client::new(None, None);
NodeService { local_peer }
let lock_manager = rustfs_lock::NsLockMap::new(false, None);
NodeService {
local_peer,
lock_manager,
}
}
impl NodeService {
@@ -1526,176 +1530,199 @@ impl Node for NodeService {
async fn lock(&self, request: Request<GenerallyLockRequest>) -> Result<Response<GenerallyLockResponse>, Status> {
let request = request.into_inner();
match &serde_json::from_str::<LockArgs>(&request.args) {
Ok(args) => {
let resource = match args.resources.first() {
Some(r) => r,
None => {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some("No resource specified".to_string()),
}));
}
};
let timeout = std::time::Duration::from_secs(30);
match GLOBAL_LOCAL_SERVER.write().await.lock(resource, &args.owner, timeout).await {
Ok(result) => Ok(tonic::Response::new(GenerallyLockResponse {
success: result,
error_info: None,
})),
Err(err) => Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not lock, args: {args}, err: {err}")),
})),
}
// Parse the request to extract resource and owner
let args: serde_json::Value = match serde_json::from_str(&request.args) {
Ok(args) => args,
Err(err) => {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not decode args, err: {err}")),
}));
}
};
let resource = args["resources"][0].as_str().unwrap_or("");
let owner = args["owner"].as_str().unwrap_or("");
if resource.is_empty() {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some("No resource specified".to_string()),
}));
}
match self
.lock_manager
.lock_batch_with_ttl(&[resource.to_string()], owner, std::time::Duration::from_secs(30), Some(std::time::Duration::from_secs(30)))
.await
{
Ok(result) => Ok(tonic::Response::new(GenerallyLockResponse {
success: result,
error_info: None,
})),
Err(err) => Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not decode args, err: {err}")),
error_info: Some(format!("can not lock, resource: {resource}, owner: {owner}, err: {err}")),
})),
}
}
async fn un_lock(&self, request: Request<GenerallyLockRequest>) -> Result<Response<GenerallyLockResponse>, Status> {
let request = request.into_inner();
match &serde_json::from_str::<LockArgs>(&request.args) {
Ok(args) => {
let resource = match args.resources.first() {
Some(r) => r,
None => {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some("No resource specified".to_string()),
}));
}
};
match GLOBAL_LOCAL_SERVER.write().await.unlock(resource, &args.owner).await {
Ok(_) => Ok(tonic::Response::new(GenerallyLockResponse {
success: true,
error_info: None,
})),
Err(err) => Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not unlock, args: {args}, err: {err}")),
})),
}
let args: serde_json::Value = match serde_json::from_str(&request.args) {
Ok(args) => args,
Err(err) => {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not decode args, err: {err}")),
}));
}
};
let resource = args["resources"][0].as_str().unwrap_or("");
let owner = args["owner"].as_str().unwrap_or("");
if resource.is_empty() {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some("No resource specified".to_string()),
}));
}
match self.lock_manager.unlock_batch(&[resource.to_string()], owner).await {
Ok(_) => Ok(tonic::Response::new(GenerallyLockResponse {
success: true,
error_info: None,
})),
Err(err) => Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not decode args, err: {err}")),
error_info: Some(format!("can not unlock, resource: {resource}, owner: {owner}, err: {err}")),
})),
}
}
async fn r_lock(&self, request: Request<GenerallyLockRequest>) -> Result<Response<GenerallyLockResponse>, Status> {
let request = request.into_inner();
match &serde_json::from_str::<LockArgs>(&request.args) {
Ok(args) => {
let resource = match args.resources.first() {
Some(r) => r,
None => {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some("No resource specified".to_string()),
}));
}
};
let timeout = std::time::Duration::from_secs(30);
match GLOBAL_LOCAL_SERVER.write().await.rlock(resource, &args.owner, timeout).await {
Ok(result) => Ok(tonic::Response::new(GenerallyLockResponse {
success: result,
error_info: None,
})),
Err(err) => Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not rlock, args: {args}, err: {err}")),
})),
}
let args: serde_json::Value = match serde_json::from_str(&request.args) {
Ok(args) => args,
Err(err) => {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not decode args, err: {err}")),
}));
}
};
let resource = args["resources"][0].as_str().unwrap_or("");
let owner = args["owner"].as_str().unwrap_or("");
if resource.is_empty() {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some("No resource specified".to_string()),
}));
}
match self
.lock_manager
.rlock_batch_with_ttl(&[resource.to_string()], owner, std::time::Duration::from_secs(30), Some(std::time::Duration::from_secs(30)))
.await
{
Ok(result) => Ok(tonic::Response::new(GenerallyLockResponse {
success: result,
error_info: None,
})),
Err(err) => Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not decode args, err: {err}")),
error_info: Some(format!("can not rlock, resource: {resource}, owner: {owner}, err: {err}")),
})),
}
}
async fn r_un_lock(&self, request: Request<GenerallyLockRequest>) -> Result<Response<GenerallyLockResponse>, Status> {
let request = request.into_inner();
match &serde_json::from_str::<LockArgs>(&request.args) {
Ok(args) => {
let resource = match args.resources.first() {
Some(r) => r,
None => {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some("No resource specified".to_string()),
}));
}
};
match GLOBAL_LOCAL_SERVER.write().await.runlock(resource, &args.owner).await {
Ok(_) => Ok(tonic::Response::new(GenerallyLockResponse {
success: true,
error_info: None,
})),
Err(err) => Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not runlock, args: {args}, err: {err}")),
})),
}
let args: serde_json::Value = match serde_json::from_str(&request.args) {
Ok(args) => args,
Err(err) => {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not decode args, err: {err}")),
}));
}
};
let resource = args["resources"][0].as_str().unwrap_or("");
let owner = args["owner"].as_str().unwrap_or("");
if resource.is_empty() {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some("No resource specified".to_string()),
}));
}
match self.lock_manager.runlock_batch(&[resource.to_string()], owner).await {
Ok(_) => Ok(tonic::Response::new(GenerallyLockResponse {
success: true,
error_info: None,
})),
Err(err) => Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not decode args, err: {err}")),
error_info: Some(format!("can not runlock, resource: {resource}, owner: {owner}, err: {err}")),
})),
}
}
async fn force_un_lock(&self, request: Request<GenerallyLockRequest>) -> Result<Response<GenerallyLockResponse>, Status> {
let request = request.into_inner();
match &serde_json::from_str::<LockArgs>(&request.args) {
Ok(args) => {
let resource = match args.resources.first() {
Some(r) => r,
None => {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some("No resource specified".to_string()),
}));
}
};
match GLOBAL_LOCAL_SERVER.write().await.unlock(resource, &args.owner).await {
Ok(_) => Ok(tonic::Response::new(GenerallyLockResponse {
success: true,
error_info: None,
})),
Err(err) => Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not force_unlock, args: {args}, err: {err}")),
})),
}
let args: serde_json::Value = match serde_json::from_str(&request.args) {
Ok(args) => args,
Err(err) => {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not decode args, err: {err}")),
}));
}
};
let resource = args["resources"][0].as_str().unwrap_or("");
let owner = args["owner"].as_str().unwrap_or("");
if resource.is_empty() {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some("No resource specified".to_string()),
}));
}
match self.lock_manager.unlock_batch(&[resource.to_string()], owner).await {
Ok(_) => Ok(tonic::Response::new(GenerallyLockResponse {
success: true,
error_info: None,
})),
Err(err) => Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not decode args, err: {err}")),
error_info: Some(format!("can not force_unlock, resource: {resource}, owner: {owner}, err: {err}")),
})),
}
}
async fn refresh(&self, request: Request<GenerallyLockRequest>) -> Result<Response<GenerallyLockResponse>, Status> {
let request = request.into_inner();
match &serde_json::from_str::<LockArgs>(&request.args) {
Ok(_args) => {
// 本地锁不需要刷新
Ok(tonic::Response::new(GenerallyLockResponse {
success: true,
error_info: None,
}))
let _args: serde_json::Value = match serde_json::from_str(&request.args) {
Ok(args) => args,
Err(err) => {
return Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not decode args, err: {err}")),
}));
}
Err(err) => Ok(tonic::Response::new(GenerallyLockResponse {
success: false,
error_info: Some(format!("can not decode args, err: {err}")),
})),
}
};
Ok(tonic::Response::new(GenerallyLockResponse {
success: true,
error_info: None,
}))
}
async fn local_storage_info(
@@ -3683,15 +3710,15 @@ mod tests {
// Note: signal_service test is skipped because it contains todo!() and would panic
#[test]
fn test_node_service_debug() {
#[tokio::test]
async fn test_node_service_debug() {
let service = create_test_node_service();
let debug_str = format!("{service:?}");
assert!(debug_str.contains("NodeService"));
}
#[test]
fn test_node_service_creation() {
#[tokio::test]
async fn test_node_service_creation() {
let service1 = make_server();
let service2 = make_server();
+3 -6
View File
@@ -83,7 +83,7 @@ use rustfs_filemeta::{
headers::{AMZ_OBJECT_TAGGING, AMZ_STORAGE_CLASS},
merge_file_meta_versions,
};
use rustfs_lock::{LockApi, NsLockMap};
use rustfs_lock::{NamespaceLockManager, NsLockMap};
use rustfs_madmin::heal_commands::{HealDriveInfo, HealResultItem};
use rustfs_rio::{EtagResolvable, HashReader, TryGetIndex as _, WarpReader};
use rustfs_utils::{
@@ -123,7 +123,7 @@ pub const MAX_PARTS_COUNT: usize = 10000;
#[derive(Clone, Debug)]
pub struct SetDisks {
pub lockers: Vec<LockApi>,
pub lockers: Vec<Arc<rustfs_lock::NamespaceLock>>,
pub locker_owner: String,
pub ns_mutex: Arc<NsLockMap>,
pub disks: Arc<RwLock<Vec<Option<DiskStore>>>>,
@@ -138,7 +138,7 @@ pub struct SetDisks {
impl SetDisks {
#[allow(clippy::too_many_arguments)]
pub async fn new(
lockers: Vec<LockApi>,
lockers: Vec<Arc<rustfs_lock::NamespaceLock>>,
locker_owner: String,
ns_mutex: Arc<NsLockMap>,
disks: Arc<RwLock<Vec<Option<DiskStore>>>>,
@@ -4066,7 +4066,6 @@ impl ObjectIO for SetDisks {
async fn put_object(&self, bucket: &str, object: &str, data: &mut PutObjReader, opts: &ObjectOptions) -> Result<ObjectInfo> {
let disks = self.disks.read().await;
// 获取对象锁
let mut _ns = None;
if !opts.no_lock {
let paths = vec![object.to_string()];
@@ -4076,7 +4075,6 @@ impl ObjectIO for SetDisks {
.await
.map_err(|err| Error::other(err.to_string()))?;
// 尝试获取锁
let lock_acquired = ns_lock
.lock_batch(&paths, &self.locker_owner, std::time::Duration::from_secs(5))
.await
@@ -4293,7 +4291,6 @@ impl ObjectIO for SetDisks {
self.delete_all(RUSTFS_META_TMP_BUCKET, &tmp_dir).await?;
// 释放对象锁
if let Some(ns_lock) = _ns {
let paths = vec![object.to_string()];
if let Err(err) = ns_lock.unlock_batch(&paths, &self.locker_owner).await {
+31 -11
View File
@@ -40,10 +40,11 @@ use crate::{
store_init::{check_format_erasure_values, get_format_erasure_in_quorum, load_format_erasure_all, save_format_file},
};
use futures::future::join_all;
use futures_util::FutureExt;
use http::HeaderMap;
use rustfs_common::globals::GLOBAL_Local_Node_Name;
use rustfs_filemeta::FileInfo;
use rustfs_lock::{LockApi, NsLockMap, new_lock_api};
use rustfs_lock::NamespaceLock;
use rustfs_madmin::heal_commands::{HealDriveInfo, HealResultItem};
use rustfs_utils::{crc_hash, path::path_join_buf, sip_hash};
use tokio::sync::RwLock;
@@ -60,7 +61,7 @@ pub struct Sets {
pub id: Uuid,
// pub sets: Vec<Objects>,
// pub disk_set: Vec<Vec<Option<DiskStore>>>, // [set_count_idx][set_drive_count_idx] = disk_idx
pub lockers: Vec<Vec<LockApi>>,
pub lockers: Vec<Vec<Arc<NamespaceLock>>>,
pub disk_set: Vec<Arc<SetDisks>>, // [set_count_idx][set_drive_count_idx] = disk_idx
pub pool_idx: usize,
pub endpoints: PoolEndpoints,
@@ -93,23 +94,42 @@ impl Sets {
let set_count = fm.erasure.sets.len();
let set_drive_count = fm.erasure.sets[0].len();
let mut unique: Vec<Vec<String>> = vec![vec![]; set_count];
let mut lockers: Vec<Vec<LockApi>> = vec![vec![]; set_count];
endpoints.endpoints.as_ref().iter().enumerate().for_each(|(idx, endpoint)| {
let mut unique: Vec<Vec<String>> = (0..set_count).map(|_| vec![]).collect();
let mut lockers: Vec<Vec<Arc<NamespaceLock>>> = (0..set_count).map(|_| vec![]).collect();
for (idx, endpoint) in endpoints.endpoints.as_ref().iter().enumerate() {
let set_idx = idx / set_drive_count;
if endpoint.is_local && !unique[set_idx].contains(&"local".to_string()) {
unique[set_idx].push("local".to_string());
lockers[set_idx].push(new_lock_api(true, None));
let local_manager = rustfs_lock::NsLockMap::new(false, None);
let local_lock = Arc::new(local_manager.new_nslock(None).await.unwrap_or_else(|_| {
// If creation fails, create an empty lock manager
rustfs_lock::NsLockMap::new(false, None)
.new_nslock(None)
.now_or_never()
.unwrap()
.unwrap()
}));
lockers[set_idx].push(local_lock);
}
if !endpoint.is_local {
let host_port = format!("{}:{}", endpoint.url.host_str().unwrap(), endpoint.url.port().unwrap());
if !unique[set_idx].contains(&host_port) {
unique[set_idx].push(host_port);
lockers[set_idx].push(new_lock_api(false, Some(endpoint.url.clone())));
let dist_manager = rustfs_lock::NsLockMap::new(true, None);
let dist_lock = Arc::new(dist_manager.new_nslock(Some(endpoint.url.clone())).await.unwrap_or_else(|_| {
// If creation fails, create an empty lock manager
rustfs_lock::NsLockMap::new(true, None)
.new_nslock(Some(endpoint.url.clone()))
.now_or_never()
.unwrap()
.unwrap()
}));
lockers[set_idx].push(dist_lock);
}
}
});
}
let mut disk_set = Vec::with_capacity(set_count);
@@ -170,7 +190,7 @@ impl Sets {
let set_disks = SetDisks::new(
locker.clone(),
GLOBAL_Local_Node_Name.read().await.to_string(),
Arc::new(NsLockMap::new(is_dist_erasure().await, None)),
Arc::new(rustfs_lock::NsLockMap::new(is_dist_erasure().await, None)),
Arc::new(RwLock::new(set_drive)),
set_drive_count,
parity_count,
@@ -543,7 +563,7 @@ impl StorageAPI for Sets {
objects: Vec<ObjectToDelete>,
opts: ObjectOptions,
) -> Result<(Vec<DeletedObject>, Vec<Option<Error>>)> {
// 默认返回值
// Default return value
let mut del_objects = vec![DeletedObject::default(); objects.len()];
let mut del_errs = Vec::with_capacity(objects.len());
@@ -602,7 +622,7 @@ impl StorageAPI for Sets {
// del_errs.extend(errs);
// }
// TODO: 并发
// TODO: Implement concurrency
for (k, v) in set_obj_map {
let disks = self.get_disks(k);
let objs: Vec<ObjectToDelete> = v.iter().map(|v| v.obj.clone()).collect();