diff --git a/crates/heal/Cargo.toml b/crates/heal/Cargo.toml index 51d0132ac..e213c7da5 100644 --- a/crates/heal/Cargo.toml +++ b/crates/heal/Cargo.toml @@ -26,6 +26,9 @@ documentation = "https://docs.rs/rustfs-heal/latest/rustfs_heal/" keywords = ["RustFS", "heal", "erasure-coding", "Minio"] categories = ["web-programming", "development-tools", "filesystem"] +[lints] +workspace = true + [dependencies] rustfs-config = { workspace = true } rustfs-concurrency = { workspace = true } @@ -53,7 +56,7 @@ serial_test = { workspace = true } tempfile = { workspace = true } walkdir = { workspace = true } http = { workspace = true } -temp-env = { workspace = true } +temp-env = { workspace = true, features = ["async_closure"] } tokio = { workspace = true, features = ["test-util", "fs", "rt-multi-thread"] } [lib] diff --git a/crates/heal/src/heal/channel.rs b/crates/heal/src/heal/channel.rs index 9c6385aa7..5656db38c 100644 --- a/crates/heal/src/heal/channel.rs +++ b/crates/heal/src/heal/channel.rs @@ -482,7 +482,7 @@ impl HealChannelProcessor { request_id: client_token, success: false, data: None, - error: Some(error_text.clone()), + error: Some(error_text), }; let _ = response_tx.send(Ok(response.clone())); self.publish_response(response); diff --git a/crates/heal/src/heal/event.rs b/crates/heal/src/heal/event.rs index 0a6d20756..0d46b859b 100644 --- a/crates/heal/src/heal/event.rs +++ b/crates/heal/src/heal/event.rs @@ -576,7 +576,7 @@ mod tests { assert_eq!(handler.event_count(), 1); handler.add_event(event.clone()); - handler.add_event(event.clone()); + handler.add_event(event); assert_eq!(handler.event_count(), 3); } @@ -593,7 +593,7 @@ mod tests { handler.add_event(event.clone()); handler.add_event(event.clone()); - handler.add_event(event.clone()); // Should remove oldest + handler.add_event(event); // Should remove oldest assert_eq!(handler.event_count(), 2); } @@ -610,7 +610,7 @@ mod tests { }; handler.add_event(event.clone()); - handler.add_event(event.clone()); + handler.add_event(event); let events = handler.get_events(); assert_eq!(events.len(), 2); diff --git a/crates/heal/src/heal/manager.rs b/crates/heal/src/heal/manager.rs index f1ff61beb..5f0d15c4f 100644 --- a/crates/heal/src/heal/manager.rs +++ b/crates/heal/src/heal/manager.rs @@ -2892,7 +2892,7 @@ fn update_task_running_metric_for_task(active_heals: &HashMap type_label.to_string(), - "set" => set_label.clone() + "set" => set_label ) .set(count as f64); } @@ -3482,7 +3482,7 @@ mod tests { ); assert_eq!(queue.push(blocked), QueuePushOutcome::Accepted); - assert_eq!(queue.push(runnable.clone()), QueuePushOutcome::Accepted); + assert_eq!(queue.push(runnable), QueuePushOutcome::Accepted); let mut running = HashMap::new(); running.insert("pool_0_set_1".to_string(), 1); diff --git a/crates/heal/tests/heal_b920_subquorum_union_test.rs b/crates/heal/tests/heal_b920_subquorum_union_test.rs index 9f6a4b863..f3121aaa3 100644 --- a/crates/heal/tests/heal_b920_subquorum_union_test.rs +++ b/crates/heal/tests/heal_b920_subquorum_union_test.rs @@ -28,6 +28,7 @@ use rustfs_heal::heal::storage::{ }; use serial_test::serial; use std::{ + future::Future, path::{Path, PathBuf}, sync::Arc, }; @@ -52,15 +53,9 @@ fn versioned_test_data(seed: u8) -> Vec { .collect() } -/// Disable the dangling-delete grace window so the destructive path is genuinely -/// LIVE in these tests: without the decision-1 guard, a recoverable version WOULD -/// be dangling-deleted here. With grace at its 1h default the delete path would be -/// masked and the test would prove nothing. -fn disable_dangling_grace() { - // Safe under nextest: each test runs in its own process and is `#[serial]`. - unsafe { - std::env::set_var(GRACE_ENV, "0"); - } +/// Disable the grace window while the destructive heal decision is evaluated. +async fn with_dangling_grace_disabled(future: impl Future) -> T { + temp_env::async_with_vars([(GRACE_ENV, Some("0"))], future).await } /// Build a real N-disk single-set `ECStore` + `ECStoreHealStorage` via the @@ -250,7 +245,6 @@ mod serial_tests { #[tokio::test(flavor = "multi_thread", worker_threads = 4)] #[serial] async fn union_meta_lost_data_present_is_repaired_not_destroyed() { - disable_dangling_grace(); let (disk_paths, ecstore, heal_storage) = heal_env_n(8).await; let bucket = "b920-meta-lost"; let object = "obj.bin"; @@ -268,10 +262,10 @@ mod serial_tests { } // Heal the version through the real heal storage (Deep). - let (_result, error) = heal_storage - .heal_object(bucket, object, Some(&v1), &deep_heal_opts()) - .await - .expect("heal_object call must not itself error"); + let (_result, error) = + with_dangling_grace_disabled(heal_storage.heal_object(bucket, object, Some(&v1), &deep_heal_opts())) + .await + .expect("heal_object call must not itself error"); assert!( error.is_none(), "recoverable version must heal without error (must NOT be dangling-deleted): {error:?}" @@ -300,7 +294,6 @@ mod serial_tests { #[tokio::test(flavor = "multi_thread", worker_threads = 4)] #[serial] async fn deep_heal_torn_minority_is_dangling_deleted_with_grace_zero() { - disable_dangling_grace(); let (disk_paths, ecstore, heal_storage) = heal_env_n(4).await; let bucket = "b920-torn"; let object = "obj.bin"; @@ -318,10 +311,10 @@ mod serial_tests { // is a real destructive action, not a no-op). assert_eq!(count_part_files(&object_dir(&disk_paths[0], bucket, object)), 1); - let (_result, error) = heal_storage - .heal_object(bucket, object, Some(&v1), &deep_heal_opts()) - .await - .expect("heal_object call must not itself error"); + let (_result, error) = + with_dangling_grace_disabled(heal_storage.heal_object(bucket, object, Some(&v1), &deep_heal_opts())) + .await + .expect("heal_object call must not itself error"); // A dangling delete reports the version as gone (FileVersionNotFound), // proving the destructive path fired for a genuinely torn write. assert!(error.is_some(), "a torn (< data_blocks) version must NOT be silently treated as healed"); @@ -349,7 +342,6 @@ mod serial_tests { #[tokio::test(flavor = "multi_thread", worker_threads = 4)] #[serial] async fn deep_heal_restores_subquorum_but_reconstructable_version_wider_set() { - disable_dangling_grace(); let (disk_paths, ecstore, heal_storage) = heal_env_n(8).await; let bucket = "b920-reconstruct"; let object = "obj.bin"; @@ -373,10 +365,10 @@ mod serial_tests { "disk-walk must enumerate the reconstructable sub-quorum version" ); - let (_result, error) = heal_storage - .heal_object(bucket, object, Some(&v1), &deep_heal_opts()) - .await - .expect("heal_object call must not itself error"); + let (_result, error) = + with_dangling_grace_disabled(heal_storage.heal_object(bucket, object, Some(&v1), &deep_heal_opts())) + .await + .expect("heal_object call must not itself error"); assert!(error.is_none(), "reconstructable version must heal cleanly: {error:?}"); // part.* + xl.meta physically restored on the 4 wiped disks. @@ -463,7 +455,6 @@ mod serial_tests { #[tokio::test(flavor = "multi_thread", worker_threads = 4)] #[serial] async fn offline_disk_during_walk_does_not_dangling_delete() { - disable_dangling_grace(); let (disk_paths, ecstore, heal_storage) = heal_env_n(4).await; let bucket = "b920-offline"; let object = "obj.bin"; @@ -491,8 +482,7 @@ mod serial_tests { remove: false, ..Default::default() }; - let (_result, error) = heal_storage - .heal_object(bucket, object, Some(&v1), &normal_opts) + let (_result, error) = with_dangling_grace_disabled(heal_storage.heal_object(bucket, object, Some(&v1), &normal_opts)) .await .expect("heal_object call must not itself error"); assert!(error.is_none(), "quorum-present object must not be destroyed: {error:?}"); @@ -510,7 +500,6 @@ mod serial_tests { #[tokio::test(flavor = "multi_thread", worker_threads = 4)] #[serial] async fn deep_heal_keeps_present_ec2_plus_2_shards_healthy() { - disable_dangling_grace(); let (disk_paths, ecstore, heal_storage) = heal_env_n(4).await; let bucket = "b1044-deep-verify"; let object = "obj.bin"; @@ -524,10 +513,10 @@ mod serial_tests { assert_eq!(count_part_files(&object_dir(disk, bucket, object)), 1, "intact shard must remain present"); } - let (_result, error) = heal_storage - .heal_object(bucket, object, Some(&v1), &deep_heal_opts()) - .await - .expect("deep heal_object call must not itself error"); + let (_result, error) = + with_dangling_grace_disabled(heal_storage.heal_object(bucket, object, Some(&v1), &deep_heal_opts())) + .await + .expect("deep heal_object call must not itself error"); assert!(error.is_none(), "Deep heal must retain the three intact EC2+2 shards: {error:?}"); assert!(