mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-19 02:56:18 +00:00
3531abb34a
B5 switched heal enumeration to list_object_versions, which only reflects the read-quorum metadata view: a version present on fewer than read-quorum disks was never enumerated, so it was never healed. Add a per-erasure-set disk-walk UNION enumerator (mirrors MinIO global-heal.go objQuorum=1 listPathRaw + mergeXLV2Versions) that surfaces every (object, version) present on ANY disk and feeds each to the existing per-version heal_object. - filemeta: MetaCacheEntries::resolve_union (dir_quorum=1/obj_quorum=1) yields the cross-disk version union at one tested seam. - ecstore: SetDisks::heal_walk_versions_page (list_path_raw fan-out, min_disks=1, dual object/version page bound, inclusive-forward de-overlap) + ECStore delegator + HealWalkVersion. - ecstore data-safety guard: before dangling-delete, try_regenerate_recoverable_meta physically probes part files via check_parts; when >= data_blocks data shards survive (meta lost but data recoverable) it regenerates xl.meta from a surviving FileInfo with the correct per-disk shard index instead of dangling-deleting. Genuine torn writes (< data_blocks) keep the current behavior — no resurrection. - heal: dw1: forward-marker cursor codec (reuses ResumeState.resume_cursor, idempotent restart on foreign tokens); list_versions_for_heal_page_disk_walk trait method (default falls back to the B5 read-quorum path); heal_bucket_with_resume selects the disk-walk enumerator when scan_mode==Deep || source==AutoHeal, else the unchanged B5 path; anti-loop guard aborts on (empty && truncated). Closes rustfs/backlog#920
51 lines
2.0 KiB
Rust
51 lines
2.0 KiB
Rust
// Copyright 2024 RustFS Team
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! Store-level entry point for the per-erasure-set disk-walk UNION heal
|
|
//! enumerator (backlog#920). Bounds-checks `(pool_idx, set_idx)` (mirroring
|
|
//! `handle_get_disks`) and delegates to the target `SetDisks`.
|
|
|
|
use super::*;
|
|
pub use crate::set_disk::HealWalkVersion;
|
|
|
|
impl ECStore {
|
|
/// Walk a specific erasure set's disks and return one page of the cross-disk
|
|
/// UNION of versions (see `SetDisks::heal_walk_versions_page`). `pool_idx` and
|
|
/// `set_idx` are bounds-checked against the live pool topology.
|
|
#[instrument(level = "debug", skip(self))]
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub async fn heal_walk_versions_page(
|
|
&self,
|
|
pool_idx: usize,
|
|
set_idx: usize,
|
|
bucket: &str,
|
|
prefix: &str,
|
|
forward_to: Option<&str>,
|
|
batch_objects: usize,
|
|
version_budget: usize,
|
|
) -> Result<(Vec<HealWalkVersion>, Option<String>, bool)> {
|
|
if pool_idx >= self.pools.len() || set_idx >= self.pools[pool_idx].disk_set.len() {
|
|
return Err(Error::other(format!(
|
|
"heal disk-walk: invalid erasure set (pool index {pool_idx}, set index {set_idx}, pool count {})",
|
|
self.pools.len()
|
|
)));
|
|
}
|
|
|
|
self.pools[pool_idx].disk_set[set_idx]
|
|
.heal_walk_versions_page(bucket, prefix, forward_to, batch_objects, version_budget)
|
|
.await
|
|
.map_err(Error::from)
|
|
}
|
|
}
|