Expand description
Your workflows under the simulator.
A Scenario registers your workflow functions, the starts and signals
a client sends, and checks on the results. Each seed runs it on a
simulated machine: the real shard runtime and Rust SDK, a seeded
scheduler, a disk that tears unflushed writes and fails flushes, process
crashes at random moments, delayed messages between shards, and a clock
that skips ahead whenever nothing is runnable, so a workflow that sleeps
for a month finishes in milliseconds. The seed also picks the shard
count, park and checkpoint timing, segment size, log format and payload
encryption, so a range of seeds covers configurations as well as
interleavings.
After every recovery and at the end the simulator checks what the engine
promises: an acknowledged start or signal is never lost, a reported
result never changes, no run stops on a determinism error when its
journal is replayed (nondeterministic workflow code shows up here), a
stream reader (Setup::read_stream) never sees a chunk change or go
away, and once faults stop every workflow finishes. Your checks then see the final
results and whatever state your step bodies kept.
use fusee::{Ctx, Error};
use fusee_sim::scenario::Scenario;
use std::sync::{Arc, Mutex};
use std::time::Duration;
let scenario = Scenario::new(|sim| {
// State that outlives crashes, like an external system: the ledger.
let ledger = Arc::new(Mutex::new(Vec::<(String, u64)>::new()));
let l = ledger.clone();
sim.register("pay", move |ctx: Ctx, amount: u64| {
let l = l.clone();
async move {
ctx.sleep("a-day", Duration::from_secs(86_400)).await?;
ctx.step("charge", || {
let l = l.clone();
let key = ctx.key();
async move {
fusee_sim::sleep(Duration::from_millis(3)).await;
let mut l = l.lock().unwrap();
if !l.iter().any(|(k, _)| *k == key) {
l.push((key, amount));
}
Ok::<_, Error>(amount)
}
})
.await
}
});
for i in 0..20 {
let at = Duration::from_millis(sim.random(500));
sim.start(at, &format!("pay-{i}"), "pay", &(i * 10));
}
sim.check(move |out| {
for i in 0..20u64 {
assert_eq!(out.result::<u64>(&format!("pay-{i}")), Some(Ok(i * 10)));
}
assert_eq!(ledger.lock().unwrap().len(), 20);
Ok(())
});
});
scenario.run_seeds(0..200).unwrap();Step bodies run on the simulator’s single thread: wait with
crate::sleep, not tokio::time::sleep, and stand in for networks
and databases with in-memory state like the ledger above. Workflow code
itself needs no change; the same async fn can be registered with
[fusee::Engine::workflow] and here.
Structs§
- Call
Reply - The answer to one object call (
Setup::call). - Outcome
- The final results of one seed.
- Scenario
- Your workflows and requests, run under seeds. See the module docs.
- Setup
- What a scenario’s setup function builds for one seed: the workflows, the client’s requests and the checks.
- Summary
- Totals over the seeds
Scenario::run_seedsran. - User
Setup - A scenario’s setup function, carried by
SimConfig::user.
Type Aliases§
- Check
- A check on the outcome of one seed. An
Err(or a panic) fails the seed.