Skip to main content

InMemoryStorage

Struct InMemoryStorage 

Source
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 contents
  • written: 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 simulation
  • pending_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

Source

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 bytes
  • seed - Seed for deterministic random generation (used for unwritten sector fill)
Source

pub fn size(&self) -> u64

Get the total size of the storage in bytes.

Source

pub fn num_sectors(&self) -> usize

Get the number of sectors in this storage.

Source

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.

Source

pub fn read(&self, offset: u64, buf: &mut [u8]) -> Result<()>

Read data from storage, applying faults as needed.

§Fault Application
  1. Unwritten sectors are filled with deterministic random data
  2. Faulted sectors have deterministic corruption applied
  3. Active overlays are applied on top
§Arguments
  • offset - Starting byte offset
  • buf - Buffer to read into
§Errors

Returns an error if the read would go past the end of storage.

Source

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 offset
  • data - Data to write
  • is_synced - If false, write is added to pending writes (may be lost on crash)
§Errors

Returns an error on overflow.

Source

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.

Source

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:

  1. Overlay 1: intended target shows old data on read
  2. Overlay 2: mistaken target shows new data on read
  3. Pristine memory is updated at intended target
§Arguments
  • intended_offset - Where the write should have gone
  • mistaken_offset - Where the write actually went
  • data - The data that was written
§Errors

Returns an error on overflow.

Source

pub fn clear_overlays(&mut self)

Clear all active overlays.

Call this to reset the misdirection state.

Source

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.

Source

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 offset
  • data - Data that “appeared” to be written
Source

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
Source

pub fn set_fault(&mut self, sector: usize)

Manually set a sector as faulted.

Useful for testing specific fault scenarios.

Source

pub fn clear_fault(&mut self, sector: usize)

Manually clear a sector’s fault.

Source

pub fn has_fault(&self, sector: usize) -> bool

Check if a sector has a fault.

Source

pub fn is_written(&self, sector: usize) -> bool

Check if a sector has been written.

Trait Implementations§

Source§

impl Debug for InMemoryStorage

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more