Skip to main content

SimulationBuilder

Struct SimulationBuilder 

Source
pub struct SimulationBuilder { /* private fields */ }
Expand description

Builder pattern for configuring and running simulation experiments.

Implementations§

Source§

impl SimulationBuilder

Source

pub fn new() -> Self

Create a new empty simulation builder.

Source

pub fn workload(self, w: impl Workload) -> Self

Add a single workload instance to the simulation.

The instance is reused across iterations (the run() method is called each iteration on the same struct). Gets client_id = 0, client_count = 1.

Source

pub fn workload_with_client_id( self, client_id: ClientId, w: impl Workload, ) -> Self

Add a single workload instance with a custom client ID strategy.

Like workload(), but the resolved client ID is available via SimContext::client_id().

Source

pub fn processes( self, count: impl Into<ProcessCount>, factory: impl Fn() -> Box<dyn Process> + 'static, ) -> Self

Add server processes to the simulation.

Processes represent the system under test — they can be killed and restarted (rebooted). A fresh instance is created from the factory on every boot.

The count parameter accepts either a fixed usize or a RangeInclusive<usize> for seeded random count per iteration.

Only one .processes() call is supported per builder. Subsequent calls overwrite the previous one.

§Examples
// Fixed 3 server processes
builder.processes(3, || Box::new(MyNode::new()))

// 3 to 7 processes, randomized per iteration
builder.processes(3..=7, || Box::new(MyNode::new()))
Source

pub fn tags(self, dimensions: &[(&str, &[&str])]) -> Self

Attach tag distribution to the last .processes() call.

Tags are distributed round-robin across process instances. Each tag dimension is distributed independently.

§Panics

Panics if called without a preceding .processes() call.

§Examples
// 5 processes: dc cycles east/west/eu, rack cycles r1/r2
builder.processes(5, || Box::new(MyNode::new()))
    .tags(&[
        ("dc", &["east", "west", "eu"]),
        ("rack", &["r1", "r2"]),
    ])
Source

pub fn attrition(self, config: Attrition) -> Self

Set built-in attrition for automatic process reboots during chaos phase.

Attrition randomly kills and restarts server processes. It respects max_dead to limit the number of simultaneously dead processes.

Requires .phases() — attrition injectors only run during the chaos phase. Without a phase config, the injector will not be spawned.

For custom fault injection, use .fault() with a FaultInjector instead.

Source

pub fn workloads( self, count: WorkloadCount, factory: impl Fn(usize) -> Box<dyn Workload> + 'static, ) -> Self

Add multiple workload instances from a factory.

The factory receives an instance index (0-based) and must return a fresh workload. Instances are created each iteration and dropped afterward. Client IDs default to sequential starting from 0 (FDB-style).

The workload is responsible for its own name() — use the index to produce unique names when count > 1 (e.g., format!("client-{i}")).

§Examples
// 3 fixed replicas
builder.workloads(WorkloadCount::Fixed(3), |i| Box::new(ReplicaWorkload::new(i)))

// 1–5 random clients
builder.workloads(WorkloadCount::Random(1..6), |i| Box::new(ClientWorkload::new(i)))
Source

pub fn workloads_with_client_id( self, count: WorkloadCount, client_id: ClientId, factory: impl Fn(usize) -> Box<dyn Workload> + 'static, ) -> Self

Add multiple workload instances with a custom client ID strategy.

Like workloads(), but client IDs are assigned according to the given ClientId strategy instead of sequential.

§Examples
// 3 clients with random IDs in [100..200)
builder.workloads_with_client_id(
    WorkloadCount::Fixed(3),
    ClientId::RandomRange(100..200),
    |i| Box::new(ClientWorkload::new(i)),
)
Source

pub fn invariant(self, i: impl Invariant) -> Self

Add an invariant to be checked after every simulation event.

Source

pub fn invariant_fn( self, name: impl Into<String>, f: impl Fn(&StateHandle, u64) + 'static, ) -> Self

Add a closure-based invariant.

Source

pub fn fault(self, f: impl FaultInjector) -> Self

Add a fault injector to run during the chaos phase.

Source

pub fn phases(self, config: PhaseConfig) -> Self

Set two-phase chaos/recovery configuration.

Source

pub fn set_iterations(self, iterations: usize) -> Self

Set the number of iterations to run.

Source

pub fn set_iteration_control(self, control: IterationControl) -> Self

Set the iteration control strategy.

Source

pub fn set_time_limit(self, duration: Duration) -> Self

Run for a specific wall-clock time duration.

Source

pub fn until_converged(self, max_iterations: usize) -> Self

Run until exploration has converged: all assert_sometimes! assertions have been reached and no new coverage was found on the last seed.

Requires .enable_exploration() to be configured. max_iterations is a safety cap to prevent infinite loops.

Source

pub fn before_iteration(self, f: impl FnMut() + 'static) -> Self

Register a callback invoked at the start of each simulation iteration.

Use this to reset shared state (directories, membership, stores) that lives outside the builder and is shared via Rc across iterations.

Source

pub fn set_debug_seeds(self, seeds: Vec<u64>) -> Self

Set specific seeds for deterministic debugging and regression testing.

Source

pub fn random_network(self) -> Self

Enable randomized network configuration for chaos testing.

Source

pub fn enable_exploration(self, config: ExplorationConfig) -> Self

Enable fork-based multiverse exploration.

When enabled, the simulation will fork child processes at assertion discovery points to explore alternate timelines with different seeds.

Source

pub fn replay_recipe(self, recipe: BugRecipe) -> Self

Set a bug recipe for deterministic replay.

The builder applies the recipe’s RNG breakpoints after its own initialization, ensuring they survive internal resets.

Source

pub fn run(self) -> SimulationReport

Run the simulation and generate a report.

Creates a fresh tokio LocalRuntime per iteration for full isolation — all tasks are killed when the runtime is dropped at iteration end.

Trait Implementations§

Source§

impl Default for SimulationBuilder

Source§

fn default() -> Self

Returns the “default value” for a type. 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