From d810dbd03d88245b395223535ccc29f56e15065f Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 30 Jun 2026 19:26:04 -0700 Subject: [PATCH] feat(scrub): dispatch EC CHECKSUM (mode 4) to checksum_scrub Accept VolumeScrubMode.CHECKSUM=4 and route it to EcVolume::checksum_scrub, accumulating blocks scanned + mismatched shards into the scrub response, plus the CHECKSUM scrub-mode metric label. Read-only bitrot verification over local shards. Claude-Session: https://claude.ai/code/session_015EE9Sc9EvNp8BCVva4RKdo --- seaweed-volume/src/server/grpc_server.rs | 33 +++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/seaweed-volume/src/server/grpc_server.rs b/seaweed-volume/src/server/grpc_server.rs index f9c76f22d..c86eed78f 100644 --- a/seaweed-volume/src/server/grpc_server.rs +++ b/seaweed-volume/src/server/grpc_server.rs @@ -37,6 +37,7 @@ fn scrub_mode_label(mode: i32) -> &'static str { 1 => "INDEX", 2 => "FULL", 3 => "LOCAL", + 4 => "CHECKSUM", _ => "UNKNOWN", } } @@ -3911,7 +3912,7 @@ impl VolumeServer for VolumeGrpcService { // Validate mode let mode = req.mode; match mode { - 1 | 2 | 3 => {} // INDEX=1, FULL=2, LOCAL=3 + 1 | 2 | 3 | 4 => {} // INDEX=1, FULL=2, LOCAL=3, CHECKSUM=4 _ => { return Err(Status::invalid_argument(format!( "unsupported EC volume scrub mode {}", @@ -4005,6 +4006,36 @@ impl VolumeServer for VolumeGrpcService { } } } + 4 => { + // CHECKSUM: verify each local shard's raw bytes against the + // bitrot checksum sidecar, exercising cold parity shards. + // Read-only. Mirrors Go's v.ChecksumScrub(). + let (blocks_scanned, broken, errs, collection) = { + let store = self.state.store.read().unwrap(); + let ecv = store.find_ec_volume(vid).ok_or_else(|| { + Status::not_found(format!("EC volume id {} not found", vid.0)) + })?; + let collection = ecv.collection.clone(); + let (blocks, broken, errs) = ecv.checksum_scrub(); + (blocks, broken, errs, collection) + }; + total_volumes += 1; + total_files += blocks_scanned; + if !errs.is_empty() || !broken.is_empty() { + broken_volume_ids.push(vid.0); + for b in broken { + broken_shard_infos.push(volume_server_pb::EcShardInfo { + volume_id: vid.0, + collection: collection.clone(), + shard_id: b, + ..Default::default() + }); + } + for msg in errs { + details.push(format!("ecvol {}: {}", vid.0, msg)); + } + } + } _ => unreachable!(), // validated above } }