mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-27 13:04:20 +01:00
This kind of fault does not semantically represent anything real, since we already have fault injection for every concrete IO operation like reading, writing, syncing and so forth. Moreover, having this feature is the direct cause of the false positive simulator failure as reported in issue #2727. There, a "run_once fault" happened immediately after we fsynced following an INSERT, which caused the simulator to think the INSERT failed, and later a sim assertion failed because the on-disk database had 1 more row than it thought it would.
123 lines
3.3 KiB
Rust
123 lines
3.3 KiB
Rust
use std::{
|
|
cell::{Cell, RefCell},
|
|
sync::Arc,
|
|
};
|
|
|
|
use rand::{RngCore, SeedableRng};
|
|
use rand_chacha::ChaCha8Rng;
|
|
use turso_core::{Clock, Instant, OpenFlags, PlatformIO, Result, IO};
|
|
|
|
use crate::runner::{clock::SimulatorClock, file::SimulatorFile};
|
|
|
|
pub(crate) struct SimulatorIO {
|
|
pub(crate) inner: Box<dyn IO>,
|
|
pub(crate) fault: Cell<bool>,
|
|
pub(crate) files: RefCell<Vec<Arc<SimulatorFile>>>,
|
|
pub(crate) rng: RefCell<ChaCha8Rng>,
|
|
pub(crate) page_size: usize,
|
|
seed: u64,
|
|
latency_probability: usize,
|
|
clock: Arc<SimulatorClock>,
|
|
}
|
|
|
|
unsafe impl Send for SimulatorIO {}
|
|
unsafe impl Sync for SimulatorIO {}
|
|
|
|
impl SimulatorIO {
|
|
pub(crate) fn new(
|
|
seed: u64,
|
|
page_size: usize,
|
|
latency_probability: usize,
|
|
min_tick: u64,
|
|
max_tick: u64,
|
|
) -> Result<Self> {
|
|
let inner = Box::new(PlatformIO::new()?);
|
|
let fault = Cell::new(false);
|
|
let files = RefCell::new(Vec::new());
|
|
let rng = RefCell::new(ChaCha8Rng::seed_from_u64(seed));
|
|
let clock = SimulatorClock::new(ChaCha8Rng::seed_from_u64(seed), min_tick, max_tick);
|
|
|
|
Ok(Self {
|
|
inner,
|
|
fault,
|
|
files,
|
|
rng,
|
|
page_size,
|
|
seed,
|
|
latency_probability,
|
|
clock: Arc::new(clock),
|
|
})
|
|
}
|
|
|
|
pub(crate) fn inject_fault(&self, fault: bool) {
|
|
self.fault.replace(fault);
|
|
for file in self.files.borrow().iter() {
|
|
file.inject_fault(fault);
|
|
}
|
|
}
|
|
|
|
pub(crate) fn print_stats(&self) {
|
|
for file in self.files.borrow().iter() {
|
|
tracing::info!(
|
|
"\n===========================\n\nPath: {}\n{}",
|
|
file.path,
|
|
file.stats_table()
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Clock for SimulatorIO {
|
|
fn now(&self) -> Instant {
|
|
self.clock.now().into()
|
|
}
|
|
}
|
|
|
|
impl IO for SimulatorIO {
|
|
fn open_file(
|
|
&self,
|
|
path: &str,
|
|
flags: OpenFlags,
|
|
_direct: bool,
|
|
) -> Result<Arc<dyn turso_core::File>> {
|
|
let inner = self.inner.open_file(path, flags, false)?;
|
|
let file = Arc::new(SimulatorFile {
|
|
path: path.to_string(),
|
|
inner,
|
|
fault: Cell::new(false),
|
|
nr_pread_faults: Cell::new(0),
|
|
nr_pwrite_faults: Cell::new(0),
|
|
nr_sync_faults: Cell::new(0),
|
|
nr_pread_calls: Cell::new(0),
|
|
nr_pwrite_calls: Cell::new(0),
|
|
nr_sync_calls: Cell::new(0),
|
|
page_size: self.page_size,
|
|
rng: RefCell::new(ChaCha8Rng::seed_from_u64(self.seed)),
|
|
latency_probability: self.latency_probability,
|
|
sync_completion: RefCell::new(None),
|
|
queued_io: RefCell::new(Vec::new()),
|
|
clock: self.clock.clone(),
|
|
});
|
|
self.files.borrow_mut().push(file.clone());
|
|
Ok(file)
|
|
}
|
|
|
|
fn remove_file(&self, path: &str) -> Result<()> {
|
|
self.files.borrow_mut().retain(|x| x.path != path);
|
|
Ok(())
|
|
}
|
|
|
|
fn run_once(&self) -> Result<()> {
|
|
let now = self.now();
|
|
for file in self.files.borrow().iter() {
|
|
file.run_queued_io(now)?;
|
|
}
|
|
self.inner.run_once()?;
|
|
Ok(())
|
|
}
|
|
|
|
fn generate_random_number(&self) -> i64 {
|
|
self.rng.borrow_mut().next_u64() as i64
|
|
}
|
|
}
|