mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-24 13:16:28 +00:00
@@ -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};
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user