implement hybrid hard-link copy strategy for directory bootstrap

Introduces recursive directory replication functions that default to hard-linking for space efficiency, with automatic fallback to full file copying on cross-filesystem failures. Enforces mandatory real copies for presence and counts subdirectories to guarantee independent data matrices before merging, while preserving the existing separation between immutable index files and mutable matrix files.
This commit is contained in:
Eric Coissac
2026-08-28 23:18:34 +02:00
parent 93fe838f97
commit 6b0c0867cc
+22 -2
View File
@@ -514,15 +514,35 @@ fn choose_base(sources: &[&KmerIndex], src_genomes: &[Vec<GenomeInfo>], mode: Me
.unwrap()
}
/// Copies `src` (the base source's whole `partitions/` tree) into `dst` at
/// bootstrap, before any actual merging happens. `merge_partition` (right
/// after) widens the pre-existing layers' `presence`/`counts` matrix files
/// in place — appending the new sources' genome columns — so those must
/// stay real, independent copies; everything else in a layer directory
/// (`mphf.bin`/`unitigs.bin`/`evidence.bin`/`unitigs.bin.idx`/
/// `fingerprint.bin`/`layer_meta.json`) is never touched again once copied
/// here, exactly the same "kmer identity vs. data matrix" split
/// `obikselect::select_layer::copy_layer_files` makes — so those are safe
/// to hard-link instead, avoiding a second full copy of a layer's largest
/// files on a big index. Falls back to a real copy per file when linking
/// itself fails (e.g. `src`/`dst` on different filesystems).
fn copy_dir_all(src: &Path, dst: &Path) -> io::Result<()> {
copy_dir_all_inner(src, dst, false)
}
fn copy_dir_all_inner(src: &Path, dst: &Path, force_copy: bool) -> io::Result<()> {
fs::create_dir_all(dst)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
let src_path = entry.path();
let dst_path = dst.join(entry.file_name());
if src_path.is_dir() {
copy_dir_all(&src_path, &dst_path)?;
} else {
// Once inside a `presence`/`counts` matrix subdirectory, every
// file below it — however deep — must be a real copy too.
let name = entry.file_name();
let is_matrix_dir = name == "presence" || name == "counts";
copy_dir_all_inner(&src_path, &dst_path, force_copy || is_matrix_dir)?;
} else if force_copy || fs::hard_link(&src_path, &dst_path).is_err() {
fs::copy(&src_path, &dst_path)?;
}
}