perf(ecstore): batch delete object lock acquisition (#2374)

Co-authored-by: 安正超 <anzhengchao@gmail.com>
This commit is contained in:
weisd
2026-04-03 21:46:51 +08:00
committed by GitHub
parent 2d91e2f580
commit 25512e2635
14 changed files with 1120 additions and 142 deletions
+104 -1
View File
@@ -21,7 +21,8 @@ use rustfs_lock::{LockClient, LockRequest};
use rustfs_protos::{
models::PingBodyBuilder,
proto_gen::node_service::{
GenerallyLockRequest, GenerallyLockResponse, PingRequest, PingResponse, node_service_server::NodeService,
BatchGenerallyLockRequest, BatchGenerallyLockResponse, GenerallyLockRequest, GenerallyLockResponse, GenerallyLockResult,
PingRequest, PingResponse, node_service_server::NodeService,
},
};
use std::pin::Pin;
@@ -33,6 +34,22 @@ use tracing::debug;
type ResponseStream<T> = Pin<Box<dyn Stream<Item = Result<T, Status>> + Send>>;
fn lock_result_from_response(response: rustfs_lock::LockResponse) -> GenerallyLockResult {
GenerallyLockResult {
success: response.success,
error_info: response.error,
lock_info: response.lock_info.and_then(|info| serde_json::to_string(&info).ok()),
}
}
fn lock_result_from_error(error: impl Into<String>) -> GenerallyLockResult {
GenerallyLockResult {
success: false,
error_info: Some(error.into()),
lock_info: None,
}
}
/// Minimal NodeService implementation that only supports Lock RPCs
/// Used for testing distributed lock scenarios with real gRPC
#[derive(Debug)]
@@ -187,6 +204,92 @@ impl NodeService for MinimalLockNodeService {
}
}
async fn lock_batch(
&self,
request: Request<BatchGenerallyLockRequest>,
) -> Result<Response<BatchGenerallyLockResponse>, Status> {
let request = request.into_inner();
let mut results = vec![lock_result_from_error("request was not processed"); request.args.len()];
let mut valid_requests = Vec::with_capacity(request.args.len());
let mut valid_indices = Vec::with_capacity(request.args.len());
for (idx, arg) in request.args.iter().enumerate() {
match serde_json::from_str::<LockRequest>(arg) {
Ok(args) => {
valid_requests.push(args);
valid_indices.push(idx);
}
Err(err) => {
results[idx] = lock_result_from_error(format!("can not decode args, err: {err}"));
}
}
}
if !valid_requests.is_empty() {
match self.lock_client.acquire_locks_batch(&valid_requests).await {
Ok(batch_results) => {
for (result_idx, response) in batch_results.into_iter().enumerate() {
if let Some(request_idx) = valid_indices.get(result_idx) {
results[*request_idx] = lock_result_from_response(response);
}
}
}
Err(err) => {
for request_idx in valid_indices {
results[request_idx] = lock_result_from_error(format!("can not batch lock, err: {err}"));
}
}
}
}
Ok(Response::new(BatchGenerallyLockResponse { results }))
}
async fn un_lock_batch(
&self,
request: Request<BatchGenerallyLockRequest>,
) -> Result<Response<BatchGenerallyLockResponse>, Status> {
let request = request.into_inner();
let mut results = vec![lock_result_from_error("request was not processed"); request.args.len()];
let mut lock_ids = Vec::with_capacity(request.args.len());
let mut valid_indices = Vec::with_capacity(request.args.len());
for (idx, arg) in request.args.iter().enumerate() {
match serde_json::from_str::<LockRequest>(arg) {
Ok(args) => {
lock_ids.push(args.lock_id);
valid_indices.push(idx);
}
Err(err) => {
results[idx] = lock_result_from_error(format!("can not decode args, err: {err}"));
}
}
}
if !lock_ids.is_empty() {
match self.lock_client.release_locks_batch(&lock_ids).await {
Ok(batch_results) => {
for (result_idx, success) in batch_results.into_iter().enumerate() {
if let Some(request_idx) = valid_indices.get(result_idx) {
results[*request_idx] = GenerallyLockResult {
success,
error_info: None,
lock_info: None,
};
}
}
}
Err(err) => {
for request_idx in valid_indices {
results[request_idx] = lock_result_from_error(format!("can not batch unlock, err: {err}"));
}
}
}
}
Ok(Response::new(BatchGenerallyLockResponse { results }))
}
// All other methods return unimplemented
async fn heal_bucket(
&self,