我有一个用 Rust 编写的云服务,它对 NFS 进行大量写入。我们注意到,在 NFS 暂存磁盘已满的[可怕]情况下,服务器没有收到此错误的通知。
阅读 Rust 文档,这似乎是预料之中的,答案是调用
sync_all
,这将返回磁盘错误:https://doc.rust-lang.org/std/fs/struct.File.html
他们说“文件超出范围时会自动关闭。Drop 的实现会忽略关闭时检测到的错误。如果必须手动处理这些错误,请使用sync_all 方法。”
所以这是可行的,但是,添加
sync_all
带来的性能下降是业务无法接受的。考虑到使用情况,变化是巨大的。例如,过去需要 5 秒的事情现在只需 25 秒。我估计在本地 SSD 上调用 sync_all
会增加 5 毫秒到 10 毫秒之间的时间。
所以我正在寻找替代解决方案,而不仅仅是打电话
sync_all
。是否有另一种快速检查磁盘空间不足错误的方法?
文档提到 Drop 的实现会忽略关闭时的错误。我可以提供一个不支持的自定义实现吗?我该怎么办呢?
这在很大程度上取决于您的实际要求,但一种可能的解决方案可能是在带外执行
sync_all
,这样您的直接工作就不会受到阻碍,但在某些时候您仍然会收到错误:
pub fn out_of_SeqCstband_cleanup(writes: usize) {
let keep_on = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
let (input, out) = std::sync::mpsc::channel::<std::fs::File>();
let cleanup = std::thread::spawn({
let keep_on = std::sync::Arc::clone(&keep_on);
move || {
while let Ok(f) = out.recv() {
if dbg!(f.sync_all()).is_err() {
// signal that an error occured, the main thread should now stop trying to write
keep_on.store(false, std::sync::atomic::Ordering::SeqCst);
}
}
}
});
for i in 0..writes {
if !keep_on.load(std::sync::atomic::Ordering::SeqCst) {
break;
}
let mut f = std::fs::File::create(format!("{i}.txt")).unwrap();
// fake some work that is being done
#[allow(deprecated)] std::thread::sleep_ms(5);
write!(f, "hello").unwrap();
input.send(f).unwrap();
}
drop(input);
cleanup.join().unwrap();
}
缺点是您无法立即知道光盘已满,但可能只有在您尝试写入更多文件后才能知道。
另一件需要注意的事情是,如果写入是瓶颈,则可能会无限期地增加通道(或者在某个时刻绑定通道最终会再次等待内联)。