feat(scrub): EC LOCAL needle walk (split from FULL) (#10144)

* feat(ec): extract locate_ec_shard_needle_interval

Mirrors Go's EcVolume.LocateEcShardNeedleInterval; reused by locate_needle
and the upcoming local scrub walk.

Claude-Session: https://claude.ai/code/session_015EE9Sc9EvNp8BCVva4RKdo

* feat(ec): add EcVolumeShard::to_ec_shard_info

Mirrors Go's ToEcShardInfo.

Claude-Session: https://claude.ai/code/session_015EE9Sc9EvNp8BCVva4RKdo

* feat(ec): add EcVolume::scrub_local

Walk the .ecx and verify each needle against the locally-held shards,
reading interval-by-interval (reusing one chunk buffer); CRC-check only
fully-local needles, report short/unreadable local shards, and abort the
scan on a structural size mismatch. Mirrors Go's EcVolume.ScrubLocal.

Claude-Session: https://claude.ai/code/session_015EE9Sc9EvNp8BCVva4RKdo

* feat(scrub): dispatch EC LOCAL (mode 3) to scrub_local

Splits the mode 2|3 arm: FULL (2) keeps the Reed-Solomon parity check;
LOCAL (3) now runs the per-needle local-shard walk.

Claude-Session: https://claude.ai/code/session_015EE9Sc9EvNp8BCVva4RKdo
This commit is contained in:
Chris Lu authored and GitHub committed 2026-06-30 01:22:46 -07:00
1 parent dccc015a1f
commit 473f7b2367
3 files changed
+237 -21

No files matched your search

+16 -2
View File
@@ -3957,8 +3957,9 @@ impl VolumeServer for VolumeGrpcService {
}
}
}
2 | 3 => {
// FULL (2) / LOCAL (3): verify EC shard data
2 => {
// FULL: Reed-Solomon parity verification over the local shards.
// (Cross-server needle verification arrives in a follow-up.)
let files = ecv.walk_ecx_stats().map(|(f, _, _)| f).unwrap_or(0);
// After cross-disk reconciliation, an EcVolume can
@@ -4013,6 +4014,19 @@ impl VolumeServer for VolumeGrpcService {
}
}
}
3 => {
// LOCAL: verify each needle against the locally-held shards.
total_volumes += 1;
let (files, shard_infos, errs) = ecv.scrub_local();
total_files += files;
if !errs.is_empty() || !shard_infos.is_empty() {
broken_volume_ids.push(vid.0);
broken_shard_infos.extend(shard_infos);
for msg in errs {
details.push(format!("ecvol {}: {}", vid.0, msg));
}
}
}
_ => unreachable!(), // validated above
}
}