fix(ecstore): harden rebalance data movement (#3234)

* fix(ecstore): harden rebalance data movement

* fix(ecstore): preserve failed rebalance status

* fix(ecstore): avoid rebalance walkdir total timeout

* fix(ecstore): retry rebalance listing timeouts

* fix(ecstore): restrict data movement resume target

* refactor(ecstore): simplify multipart movement target

* fix(ecstore): restore resume target checks

* perf(ecstore): speed up rebalance bucket merges

* fix: keep rebalance listing alive on transient failures

---------

Co-authored-by: houseme <housemecn@gmail.com>
This commit is contained in:
cxymds
2026-06-08 18:40:26 +08:00
committed by GitHub
parent 2fc1cf90e0
commit 9504dff595
11 changed files with 2673 additions and 139 deletions
+40 -1
View File
@@ -1282,6 +1282,11 @@ impl DiskAPI for RemoteDisk {
let bucket = opts.bucket.clone();
let base_dir = opts.base_dir.clone();
let disk_for_log = disk.clone();
let timeout_duration = if opts.skip_total_timeout {
Duration::ZERO
} else {
get_drive_walkdir_timeout()
};
self.execute_with_timeout_for_op_and_health_action(
"walk_dir",
@@ -1329,7 +1334,7 @@ impl DiskAPI for RemoteDisk {
Err(last_err.unwrap_or_else(|| DiskError::other("walk_dir retry exhausted without captured error")))
},
get_drive_walkdir_timeout(),
timeout_duration,
FailureHealthAction::IgnoreFailure,
)
.await
@@ -2444,6 +2449,7 @@ mod tests {
forward_to: None,
limit: 10,
disk_id: String::new(),
..Default::default()
};
let expected_body = serde_json::to_vec(&opts).unwrap();
let mut writer = Vec::new();
@@ -2463,6 +2469,37 @@ mod tests {
}
}
#[tokio::test]
async fn test_remote_disk_walk_dir_preserves_skip_total_timeout_option() {
let transport = RecordingInternodeDataTransport::default();
let remote_disk = new_remote_disk_with_transport(Arc::new(transport.clone())).await;
let opts = WalkDirOptions {
bucket: "bucket".to_string(),
base_dir: "prefix".to_string(),
recursive: true,
skip_total_timeout: true,
..Default::default()
};
let mut writer = Vec::new();
remote_disk
.walk_dir(opts, &mut writer)
.await
.expect("walk_dir should be sent through configured data transport");
let calls = transport.calls();
assert_eq!(calls.len(), 1);
match &calls[0] {
RecordedTransportCall::WalkDir(request) => {
let sent_opts: WalkDirOptions =
serde_json::from_slice(&request.body).expect("walk_dir request body should deserialize");
assert!(sent_opts.skip_total_timeout);
assert_eq!(request.stall_timeout, Some(get_drive_walkdir_stall_timeout()));
}
other => panic!("expected walk-dir transport call, got {other:?}"),
}
}
#[tokio::test]
async fn test_remote_disk_walk_dir_retries_once_on_retryable_transport_error() {
let transport = RetryingWalkDirInternodeDataTransport::with_steps(vec![
@@ -2479,6 +2516,7 @@ mod tests {
forward_to: None,
limit: 10,
disk_id: String::new(),
..Default::default()
};
let mut writer = Vec::new();
@@ -2510,6 +2548,7 @@ mod tests {
forward_to: None,
limit: 10,
disk_id: String::new(),
..Default::default()
};
let mut writer = Vec::new();