feat(ec): copy the .ecsum sidecar during VolumeEcShardsCopy

Honor copy_ecsum_file: when set, copy the generation-0 .ecsum alongside the
shards so protection travels with them, mirroring Go's non-2PC copy path.
Tolerant of a missing source (empty stream) — the 0-byte file is dropped so
mount sees no sidecar (protection off) rather than a truncated/invalid one.

Claude-Session: https://claude.ai/code/session_015EE9Sc9EvNp8BCVva4RKdo
This commit is contained in:
Chris Lu committed 2026-06-30 19:36:38 -07:00
1 parent d810dbd03d
commit 11c277765c
1 file changed
+52
+52
View File
@@ -2766,6 +2766,58 @@ impl VolumeServer for VolumeGrpcService {
}
}
// Copy the generation-0 bitrot checksum sidecar (.ecsum) when requested, so
// protection travels with the shards. Tolerant of a missing source (no-op):
// this non-2PC path has no Prepare backstop, and an unprotected source
// simply leaves the copy unprotected.
if req.copy_ecsum_file {
let copy_req = volume_server_pb::CopyFileRequest {
volume_id: req.volume_id,
collection: req.collection.clone(),
is_ec_volume: true,
ext: ".ecsum".to_string(),
compaction_revision: u32::MAX,
stop_offset: i64::MAX as u64,
ignore_source_file_not_found: true,
..Default::default()
};
let mut stream = client
.copy_file(copy_req)
.await
.map_err(|e| {
Status::internal(format!(
"VolumeEcShardsCopy volume {} copy .ecsum: {}",
vid, e
))
})?
.into_inner();
let file_path = {
let base =
crate::storage::volume::volume_file_name(&dest_dir, &req.collection, vid);
format!("{}.ecsum", base)
};
let mut file = std::fs::File::create(&file_path)
.map_err(|e| Status::internal(format!("create {}: {}", file_path, e)))?;
let mut written: u64 = 0;
while let Some(chunk) = stream
.message()
.await
.map_err(|e| Status::internal(format!("recv .ecsum: {}", e)))?
{
use std::io::Write;
file.write_all(&chunk.file_content)
.map_err(|e| Status::internal(format!("write {}: {}", file_path, e)))?;
written += chunk.file_content.len() as u64;
}
// A missing source yields an empty stream; drop the 0-byte file so mount
// sees no sidecar (protection Off) rather than a truncated/invalid one.
if written == 0 {
drop(file);
let _ = std::fs::remove_file(&file_path);
}
}
Ok(Response::new(
volume_server_pb::VolumeEcShardsCopyResponse {},
))