mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-17 18:27:49 +00:00
fix: handle scanner task failures (#4185)
This commit is contained in:
@@ -445,6 +445,10 @@ fn record_set_scan_failure(first_err: &mut Option<Error>, err: Error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn scanner_task_join_error(stage: &str, err: tokio::task::JoinError) -> Error {
|
||||||
|
Error::other(format!("{stage} task join failed: {err}"))
|
||||||
|
}
|
||||||
|
|
||||||
fn finalize_nsscanner_result(results: &[DataUsageCache], first_err: Option<Error>) -> Result<()> {
|
fn finalize_nsscanner_result(results: &[DataUsageCache], first_err: Option<Error>) -> Result<()> {
|
||||||
if results.iter().any(|result| result.info.last_update.is_some()) {
|
if results.iter().any(|result| result.info.last_update.is_some()) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -906,7 +910,21 @@ impl ScannerIO for ECStore {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let _ = join_all(wait_futs).await;
|
for join_result in join_all(wait_futs).await {
|
||||||
|
if let Err(err) = join_result {
|
||||||
|
error!(
|
||||||
|
target: "rustfs::scanner::io",
|
||||||
|
event = EVENT_SCANNER_SET_STATE,
|
||||||
|
component = LOG_COMPONENT_SCANNER,
|
||||||
|
subsystem = LOG_SUBSYSTEM_IO,
|
||||||
|
state = "set_task_join_failed",
|
||||||
|
error = %err,
|
||||||
|
"Scanner set task join failed"
|
||||||
|
);
|
||||||
|
let mut first_err = first_err_mutex.lock().await;
|
||||||
|
record_set_scan_failure(&mut first_err, scanner_task_join_error("scanner set", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
record_set_scan_concurrency_limit(0);
|
record_set_scan_concurrency_limit(0);
|
||||||
record_set_scans_queued(0);
|
record_set_scans_queued(0);
|
||||||
record_set_scans_active(0);
|
record_set_scans_active(0);
|
||||||
@@ -993,7 +1011,20 @@ impl ScannerIOCache for SetDisks {
|
|||||||
let _reset_disk_bucket_scan_gauges = DiskBucketScanGaugeReset::new(pool_label.clone(), set_label.clone());
|
let _reset_disk_bucket_scan_gauges = DiskBucketScanGaugeReset::new(pool_label.clone(), set_label.clone());
|
||||||
|
|
||||||
let mut old_cache = DataUsageCache::default();
|
let mut old_cache = DataUsageCache::default();
|
||||||
old_cache.load(self.clone(), DATA_USAGE_CACHE_NAME).await?;
|
if let Err(e) = old_cache.load(self.clone(), DATA_USAGE_CACHE_NAME).await {
|
||||||
|
warn!(
|
||||||
|
target: "rustfs::scanner::io",
|
||||||
|
event = EVENT_SCANNER_CACHE_PERSIST_STATE,
|
||||||
|
component = LOG_COMPONENT_SCANNER,
|
||||||
|
subsystem = LOG_SUBSYSTEM_IO,
|
||||||
|
pool = self.pool_index,
|
||||||
|
set = self.set_index,
|
||||||
|
cache_name = DATA_USAGE_CACHE_NAME,
|
||||||
|
state = "old_cache_load_failed",
|
||||||
|
error = %e,
|
||||||
|
"Scanner old data usage cache load failed; rebuilding from bucket caches"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let mut cache = DataUsageCache {
|
let mut cache = DataUsageCache {
|
||||||
info: DataUsageCacheInfo {
|
info: DataUsageCacheInfo {
|
||||||
@@ -1334,14 +1365,36 @@ impl ScannerIOCache for SetDisks {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = join_all(futs).await;
|
let mut first_join_err = None;
|
||||||
|
for join_result in join_all(futs).await {
|
||||||
|
if let Err(err) = join_result {
|
||||||
|
error!(
|
||||||
|
target: "rustfs::scanner::io",
|
||||||
|
event = EVENT_SCANNER_DISK_BUCKET_STATE,
|
||||||
|
component = LOG_COMPONENT_SCANNER,
|
||||||
|
subsystem = LOG_SUBSYSTEM_IO,
|
||||||
|
pool = self.pool_index,
|
||||||
|
set = self.set_index,
|
||||||
|
state = "disk_bucket_task_join_failed",
|
||||||
|
error = %err,
|
||||||
|
"Scanner disk bucket task join failed"
|
||||||
|
);
|
||||||
|
record_set_scan_failure(&mut first_join_err, scanner_task_join_error("scanner disk bucket", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
record_disk_scan_concurrency_limit(&pool_label, &set_label, 0);
|
record_disk_scan_concurrency_limit(&pool_label, &set_label, 0);
|
||||||
record_disk_bucket_scans_queued(0, &pool_label, &set_label);
|
record_disk_bucket_scans_queued(0, &pool_label, &set_label);
|
||||||
record_disk_bucket_scans_active(0, &pool_label, &set_label);
|
record_disk_bucket_scans_active(0, &pool_label, &set_label);
|
||||||
|
|
||||||
drop(bucket_result_tx_clone);
|
drop(bucket_result_tx_clone);
|
||||||
|
|
||||||
collect_bucket_results_fut.await?;
|
if let Err(err) = collect_bucket_results_fut.await {
|
||||||
|
return Err(scanner_task_join_error("scanner bucket result collector", err));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(err) = first_join_err {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
let completed_count = completed_bucket_count.load(Ordering::Relaxed);
|
let completed_count = completed_bucket_count.load(Ordering::Relaxed);
|
||||||
if should_publish_completed_snapshot(completed_count, buckets.len(), budget.budget_elapsed(), ctx.is_cancelled()) {
|
if should_publish_completed_snapshot(completed_count, buckets.len(), budget.budget_elapsed(), ctx.is_cancelled()) {
|
||||||
@@ -1746,6 +1799,19 @@ mod tests {
|
|||||||
assert!(first.to_string().contains("first"));
|
assert!(first.to_string().contains("first"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn scanner_task_join_error_includes_stage() {
|
||||||
|
let handle = tokio::spawn(async {
|
||||||
|
tokio::time::sleep(Duration::from_secs(60)).await;
|
||||||
|
});
|
||||||
|
handle.abort();
|
||||||
|
|
||||||
|
let join_err = handle.await.expect_err("aborted task should return a join error");
|
||||||
|
let err = scanner_task_join_error("scanner set", join_err);
|
||||||
|
|
||||||
|
assert!(err.to_string().contains("scanner set task join failed"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn finalize_nsscanner_result_returns_ok_when_any_set_succeeds() {
|
fn finalize_nsscanner_result_returns_ok_when_any_set_succeeds() {
|
||||||
let mut results = vec![DataUsageCache::default(), DataUsageCache::default()];
|
let mut results = vec![DataUsageCache::default(), DataUsageCache::default()];
|
||||||
|
|||||||
Reference in New Issue
Block a user