perf(delivery): release copied large source pages

This commit is contained in:
2026-10-29 17:13:17 +08:00
parent 6e20b53013
commit 4fc06ee233
3 changed files with 78 additions and 0 deletions
@@ -414,6 +414,18 @@ fn transfer(input: &mut File, output: &mut File, bytes: u64, deadline: u64) -> R
return Err(Error::Bytes);
}
total += count as u64;
if bytes > kf_contracts::restore_io::MAX_BATCH_BYTES {
// A large singleton cannot share source pages with another batch.
// Only release the already copied range of this read-only object;
// destination sync, complete readback and every witness still follow.
rustix::fs::fadvise(
&*input,
total - count as u64,
std::num::NonZeroU64::new(count as u64),
rustix::fs::Advice::DontNeed,
)
.map_err(|_| Error::Filesystem)?;
}
}
remaining(deadline)?;
if input.read(&mut [0; 1]).map_err(|_| Error::Filesystem)? != 0 {
@@ -630,4 +630,68 @@ mod tests {
assert!(request(&mut &b""[..]).is_err());
assert!(request(&mut &vec![b' '; restore_io::MAX_REQUEST_BYTES + 1][..]).is_err());
}
#[test]
fn large_singleton_keeps_complete_source_and_destination_after_cache_advice() {
let fixture = files::Fixture::new();
let roots = Arc::new(fixture.roots());
let data = vec![b's'; restore_io::MAX_BATCH_BYTES as usize + 17];
let hash = format!("{:x}", Sha256::digest(&data));
let source = fixture.source().join(&hash);
fs::write(&source, &data).unwrap();
fs::set_permissions(&source, fs::Permissions::from_mode(0o600)).unwrap();
use std::os::unix::fs::MetadataExt;
let stamp = |m: fs::Metadata| {
(
m.dev(),
m.ino(),
m.mode(),
m.nlink(),
m.uid(),
m.gid(),
m.size(),
(m.mtime(), m.mtime_nsec()),
(m.ctime(), m.ctime_nsec()),
)
};
let owner = fs::metadata(&source).unwrap();
let before = stamp(fs::metadata(&source).unwrap());
let rows = vec![restore_io::RestoreFile {
path: "artifacts/large-singleton".into(),
uid: owner.uid(),
gid: owner.gid(),
mode: 0o644,
bytes: data.len() as u64,
sha256: hash,
}];
// Validate the real wire owner policy separately from the unprivileged
// ordinary fixture's owner. Production never accepts the host test UID.
let mut wire_rows = rows.clone();
wire_rows[0].uid = 1200;
wire_rows[0].gid = 1200;
Request::Batch {
schema_version: SchemaVersion,
batch_id: 1,
files: wire_rows,
}
.validate()
.unwrap();
assert_eq!(rows.len(), 1);
let pool = Pool::new(Arc::clone(&roots)).unwrap_or_else(|_| panic!("pool"));
let verified = pool
.materialize(rows, &roots, now().unwrap() + 30_000_000_000)
.unwrap_or_else(|_| panic!("large singleton verified"));
assert_eq!(verified.len(), 1);
roots
.final_file(&verified[0])
.unwrap_or_else(|_| panic!("final witness"));
assert_eq!(
fs::read(fixture.target().join("large-singleton")).unwrap(),
data
);
assert_eq!(fs::read(&source).unwrap(), data);
assert_eq!(stamp(fs::metadata(&source).unwrap()), before);
drop(pool);
roots.unchanged().unwrap_or_else(|_| panic!("root anchors"));
}
}
+2
View File
@@ -637,3 +637,5 @@ outside public API, Git and CI artifacts.
[namespace interface](https://github.com/systemd/systemd/blob/v262/man/systemd.exec.xml),
[journal configuration](https://github.com/systemd/systemd/blob/v262/man/journald.conf.xml).
- Linux: [proc mountinfo](https://github.com/torvalds/linux/blob/v6.18/fs/proc_namespace.c).
恢复大于 64 MiB 的单对象文件时,复制器仅对已复制的只读源区间发出有界缓存回收建议,避免源和目标页面同时占满原恢复单元。目标仍在原同步屏障后完整读回、逐字节/SHA 校验,并在结束前复核全部文件身份;原内存、CPU、线程、期限和失败保留规则保持不变。