use std::{
path::Path,
sync::{
Arc,
atomic::{AtomicUsize, Ordering},
},
};
use aok::{OK, Void};
use compio::runtime::Runtime;
use log::info;
use tempfile::tempdir;
use waof::{FsyncPolicy, WalConfig, WalLog};
use wbase::pool::{AlignedBuf, BufferPool};
use wdev::SegmentedDevice;
use super::support::make_payload;
struct CountingSyncDevice {
inner: SegmentedDevice,
sync_count: AtomicUsize,
}
impl CountingSyncDevice {
fn single_file(path: &Path) -> aok::Result<Self> {
Ok(Self {
inner: SegmentedDevice::single_file(path)?,
sync_count: AtomicUsize::new(0),
})
}
}
impl wdev::Device for CountingSyncDevice {
fn sector_size(&self) -> usize {
self.inner.sector_size()
}
fn segment_size(&self) -> Option<u64> {
self.inner.segment_size()
}
fn pool(&self) -> &Arc<BufferPool> {
self.inner.pool()
}
async fn write_aligned(&self, offset: u64, buf: AlignedBuf) -> (wdev::Result<usize>, AlignedBuf) {
self.inner.write_aligned(offset, buf).await
}
async fn read_aligned(&self, offset: u64, buf: AlignedBuf) -> (wdev::Result<usize>, AlignedBuf) {
self.inner.read_aligned(offset, buf).await
}
async fn read_raw(&self, offset: u64, buf: AlignedBuf) -> (wdev::Result<usize>, AlignedBuf) {
self.inner.read_raw(offset, buf).await
}
async fn sync(&self) -> wdev::Result<()> {
self.inner.sync().await
}
async fn sync_data(&self) -> wdev::Result<()> {
self.sync_count.fetch_add(1, Ordering::AcqRel);
self.inner.sync_data().await
}
async fn truncate_until_segment(&self, segment_id: u32) -> wdev::Result<()> {
self.inner.truncate_until_segment(segment_id).await
}
}
#[test]
fn test_fsync_policy_controls_sync_data() -> Void {
let rt = Runtime::new()?;
rt.block_on(async {
let dir = tempdir()?;
let device = Arc::new(CountingSyncDevice::single_file(
&dir.path().join("always.log"),
)?);
let wal = WalLog::new(Arc::clone(&device) as Arc<_>, WalConfig::new(64 * 1024))?;
assert_eq!(wal.config().fsync, FsyncPolicy::Always);
wal.enqueue(&make_payload(64, 1))?;
wal.commit().await?;
wal.enqueue(&make_payload(64, 2))?;
wal.commit().await?;
assert_eq!(
device.sync_count.load(Ordering::Acquire),
2,
"Always 档每次提交批次须恰好一次 sync_data"
);
let device = Arc::new(CountingSyncDevice::single_file(
&dir.path().join("deferred.log"),
)?);
let config = WalConfig {
fsync: FsyncPolicy::Deferred,
..WalConfig::new(64 * 1024)
};
let wal = WalLog::new(Arc::clone(&device) as Arc<_>, config)?;
assert_eq!(wal.config().fsync, FsyncPolicy::Deferred);
wal.enqueue(&make_payload(64, 3))?;
let committed = wal.commit().await?;
assert!(committed >= 8 + 64, "Deferred 档提交水位须照常推进至记录尾");
assert_eq!(
device.sync_count.load(Ordering::Acquire),
0,
"Deferred 档提交不得触发 sync_data"
);
wal.commit().await?;
assert_eq!(device.sync_count.load(Ordering::Acquire), 0);
wal.sync().await?;
assert_eq!(device.sync_count.load(Ordering::Acquire), 1);
info!("WAL fsync 策略位测试通过");
aok::Result::<()>::Ok(())
})?;
OK
}