perf(delivery): release copied large source pages
This commit is contained in:
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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、线程、期限和失败保留规则保持不变。
|
||||
|
||||
Reference in New Issue
Block a user