pub struct InMemoryStorage { /* private fields */ }Expand description
In-memory storage with deterministic fault injection.
This struct represents a simulated storage device that can inject various faults at read time while keeping pristine data intact.
§Design
data: Pristine storage contentswritten: Tracks which sectors have been written (unwritten sectors return random data)faults: Tracks which sectors have faults (faulted sectors return corrupted data)overlays: Temporary data overlays for misdirected write simulationpending_writes: Writes that haven’t been synced yet (may be lost on crash)
§Example
use moonpool_sim::storage::memory::{InMemoryStorage, SECTOR_SIZE};
let mut storage = InMemoryStorage::new(4096, 42);
storage.write(0, b"Hello, World!", true)?;
let mut buf = vec![0u8; 13];
storage.read(0, &mut buf)?;
assert_eq!(&buf, b"Hello, World!");Implementations§
Source§impl InMemoryStorage
impl InMemoryStorage
Sourcepub fn new(size: u64, seed: u64) -> Self
pub fn new(size: u64, seed: u64) -> Self
Create a new in-memory storage with the given size and seed.
§Arguments
size- Total size of the storage in bytesseed- Seed for deterministic random generation (used for unwritten sector fill)
Sourcepub fn num_sectors(&self) -> usize
pub fn num_sectors(&self) -> usize
Get the number of sectors in this storage.
Sourcepub fn resize(&mut self, new_size: u64)
pub fn resize(&mut self, new_size: u64)
Resize the storage to a new size.
If the new size is larger, the storage is extended with zeros. If the new size is smaller, the storage is truncated.
Sourcepub fn read(&self, offset: u64, buf: &mut [u8]) -> Result<()>
pub fn read(&self, offset: u64, buf: &mut [u8]) -> Result<()>
Read data from storage, applying faults as needed.
§Fault Application
- Unwritten sectors are filled with deterministic random data
- Faulted sectors have deterministic corruption applied
- Active overlays are applied on top
§Arguments
offset- Starting byte offsetbuf- Buffer to read into
§Errors
Returns an error if the read would go past the end of storage.
Sourcepub fn write(&mut self, offset: u64, data: &[u8], is_synced: bool) -> Result<()>
pub fn write(&mut self, offset: u64, data: &[u8], is_synced: bool) -> Result<()>
Write data to storage.
The storage automatically extends to accommodate writes past the current size, similar to how real file systems work.
§Arguments
offset- Starting byte offsetdata- Data to writeis_synced- If false, write is added to pending writes (may be lost on crash)
§Errors
Returns an error on overflow.
Sourcepub fn sync(&mut self)
pub fn sync(&mut self)
Sync all pending writes, making them durable.
After sync, pending writes are cleared and won’t be affected by crash simulation.
Sourcepub fn apply_misdirected_write(
&mut self,
intended_offset: u64,
mistaken_offset: u64,
data: &[u8],
) -> Result<()>
pub fn apply_misdirected_write( &mut self, intended_offset: u64, mistaken_offset: u64, data: &[u8], ) -> Result<()>
Apply a misdirected write.
Simulates a write that lands at the wrong location. Uses the TigerBeetle 2-overlay pattern:
- Overlay 1: intended target shows old data on read
- Overlay 2: mistaken target shows new data on read
- Pristine memory is updated at intended target
§Arguments
intended_offset- Where the write should have gonemistaken_offset- Where the write actually wentdata- The data that was written
§Errors
Returns an error on overflow.
Sourcepub fn clear_overlays(&mut self)
pub fn clear_overlays(&mut self)
Clear all active overlays.
Call this to reset the misdirection state.
Sourcepub fn read_misdirected(&self, offset: u64, buf: &mut [u8]) -> Result<()>
pub fn read_misdirected(&self, offset: u64, buf: &mut [u8]) -> Result<()>
Read data from a misdirected location.
Instead of reading from offset, reads from a different location
chosen deterministically based on the seed. The caller is responsible
for deciding when to call this vs regular read().
§Arguments
offset- The intended read offset (will read from somewhere else)buf- Buffer to read into
§Errors
Returns an error if the read would go past the end of storage.
Sourcepub fn record_phantom_write(&mut self, offset: u64, data: &[u8])
pub fn record_phantom_write(&mut self, offset: u64, data: &[u8])
Record a phantom write.
A phantom write appears to succeed but the data is never actually
persisted. The data is added to pending writes with is_phantom: true,
meaning it will be lost on crash without corrupting other data.
§Arguments
offset- Starting byte offsetdata- Data that “appeared” to be written
Sourcepub fn apply_crash(&mut self, crash_fault_probability: f64)
pub fn apply_crash(&mut self, crash_fault_probability: f64)
Apply crash simulation to pending writes.
For each pending non-phantom write, there’s a chance that a sector in the write range gets marked as faulted (simulating a torn write). Phantom writes simply disappear.
§Arguments
crash_fault_probability- Probability [0.0, 1.0] that each pending write experiences a crash fault
Sourcepub fn set_fault(&mut self, sector: usize)
pub fn set_fault(&mut self, sector: usize)
Manually set a sector as faulted.
Useful for testing specific fault scenarios.
Sourcepub fn clear_fault(&mut self, sector: usize)
pub fn clear_fault(&mut self, sector: usize)
Manually clear a sector’s fault.
Sourcepub fn is_written(&self, sector: usize) -> bool
pub fn is_written(&self, sector: usize) -> bool
Check if a sector has been written.