use std::fs::File;
use std::io::{self, Seek, Write};
pub trait WalSink: Write + Seek + Send {
fn sync_data(&self) -> io::Result<()>;
fn sync_all(&self) -> io::Result<()>;
fn set_len(&self, size: u64) -> io::Result<()>;
}
impl WalSink for File {
fn sync_data(&self) -> io::Result<()> {
File::sync_data(self)
}
fn sync_all(&self) -> io::Result<()> {
File::sync_all(self)
}
fn set_len(&self, size: u64) -> io::Result<()> {
File::set_len(self, size)
}
}
#[cfg(test)]
pub(crate) struct FullDiskSink {
file: File,
budget: std::sync::Arc<std::sync::atomic::AtomicI64>,
}
#[cfg(test)]
impl FullDiskSink {
pub(crate) fn new(file: File, budget: std::sync::Arc<std::sync::atomic::AtomicI64>) -> Self {
Self { file, budget }
}
fn enospc() -> io::Error {
io::Error::new(io::ErrorKind::StorageFull, "No space left on device")
}
}
#[cfg(test)]
impl Write for FullDiskSink {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
use std::sync::atomic::Ordering;
let remaining = self.budget.load(Ordering::SeqCst);
if remaining <= 0 {
return Err(Self::enospc());
}
let n = buf
.len()
.min(usize::try_from(remaining).unwrap_or(usize::MAX));
let written = self.file.write(&buf[..n])?;
self.budget
.fetch_sub(i64::try_from(written).unwrap_or(i64::MAX), Ordering::SeqCst);
Ok(written)
}
fn flush(&mut self) -> io::Result<()> {
self.file.flush()
}
}
#[cfg(test)]
impl Seek for FullDiskSink {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
self.file.seek(pos)
}
}
#[cfg(test)]
impl WalSink for FullDiskSink {
fn sync_data(&self) -> io::Result<()> {
self.file.sync_data()
}
fn sync_all(&self) -> io::Result<()> {
self.file.sync_all()
}
fn set_len(&self, size: u64) -> io::Result<()> {
self.file.set_len(size)
}
}