Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Quickstart

Run a complete multi-node test in one command.


Prerequisites

  • Rust toolchain (the workspace pins its version via rust-toolchain.toml)
  • Unix-like system (tested on Linux and macOS)
  • For Compose examples: a running Docker daemon
  • For Kubernetes examples: a reachable cluster context

No other setup. Example node binaries are resolved automatically; the kvstore example builds its node with Cargo on first run if no prebuilt binary is available.


Your First Test

git clone <this-repository>
cd <this-repository>
cargo run -p kvstore-examples --bin kvstore_app_host_convergence

First run takes a few minutes (builds the framework and the kvstore-node binary).

What happens:

  1. AppHost::scenario() builds a scenario around a composed application instead of a managed node topology.
  2. with_app(KvLocalApp::nodes(3)) deploys a three-node kvstore cluster as local processes.
  3. The convergence workload writes a value, restarts node-0, waits for readiness, and writes again.
  4. The runner evaluates the outcome and tears the cluster down.

What you should see:

  • Three kvstore-node processes spawn with generated configs in per-run temporary directories
  • The workload logs a successful write before and after the restart
  • The command exits successfully and removes the temporary directories

The Code Behind It

The binary is short enough to read in full at examples/kvstore/examples/src/bin/app_host_convergence.rs. Its core is:

let mut scenario = AppHost::scenario()
    .with_app(KvLocalApp::nodes(3))
    .with_run_duration(Duration::from_secs(5))
    .with_workload(KvAppHostConvergence::new(3))
    .build()?;

let deployer = AppHostLocalDeployer::default();
let runner = deployer.deploy(&scenario).await?;
runner.run(&mut scenario).await?;

The workload reaches the deployed cluster through a typed handle (RunContext is the object every workload receives at run time; see Part III):

async fn start(&self, ctx: &RunContext<AppHostEnv>) -> Result<(), DynError> {
    let cluster = ctx.require_app::<LocalAppCluster<KvEnv>>()?;

    put_value(&cluster, "before-restart").await?;
    cluster.restart_node("node-0").await?;
    cluster.wait_node_ready("node-0").await?;
    put_value(&cluster, "after-restart").await?;
    Ok(())
}

The same pattern can run in #[tokio::test] functions. The composition acceptance suite does this:

cargo test -p multi-app-e2e

It uses a reusable fixture crate for the stack, workload, and expectation, then drives them from ordinary integration tests.


Where to Go Next

GoalRead
Understand the abstractions you just usedPart I — Mental Model
Compose your own application stackPart II — Composing Applications
Write workloads and expectationsPart III — Scenario Runtime
Put your own node behind the frameworkPart IV — Uniform Clusters
Run against Compose, Kubernetes, or a live networkPart V — Deployers and Sources