mosaik 0.3.13

A Rust runtime for building self-organizing, leaderless distributed systems.
Documentation
# Commands & Queries

Once you have a `Group<M>` handle, you interact with the replicated state machine through **commands** (writes) and **queries** (reads).

## Commands

Commands mutate state and are replicated to all group members through the Raft log. They are guaranteed to be applied in the same order on every node.

### `execute` — Send and Wait

Sends a command and waits for it to be committed (replicated to a quorum):

```rust,ignore
let index = group.execute(CounterCmd::Increment(5)).await?;
println!("Command committed at log index {index}");
```

If the local node is:
- **Leader**: replicates to followers, resolves when quorum acknowledges
- **Follower**: forwards to the leader, resolves when the leader commits

### `execute_many` — Batch Send and Wait

Send multiple commands atomically:

```rust,ignore
let range = group.execute_many([
    CounterCmd::Increment(1),
    CounterCmd::Increment(2),
    CounterCmd::Increment(3),
]).await?;
println!("Commands committed at indices {range:?}");
```

Returns an `IndexRange` (`RangeInclusive<Index>`) covering all committed entries.

### `feed` — Fire and Forget

Sends a command without waiting for commitment. Resolves once the leader acknowledges receipt and assigns a log index:

```rust,ignore
let index = group.feed(CounterCmd::Increment(10)).await?;
println!("Command assigned index {index}, not yet committed");
```

### `feed_many` — Batch Fire and Forget

```rust,ignore
let range = group.feed_many([
    CounterCmd::Reset,
    CounterCmd::Increment(100),
]).await?;
```

### Waiting for Commitment After Feed

Combine `feed` with the cursor watcher:

```rust,ignore
let range = group.feed_many(commands).await?;

// Wait until all commands are committed
group.when().committed().reaches(range.clone()).await;
```

### Command Errors

```rust,ignore
pub enum CommandError<M: StateMachine> {
    Offline(Vec<M::Command>),  // Node is offline; commands returned
    NoCommands,                // Empty command list
    GroupTerminated,           // Group is shut down
}
```

The `Offline` variant returns the unsent commands so they can be retried:

```rust,ignore
match group.execute(cmd).await {
    Ok(index) => println!("Committed at {index}"),
    Err(CommandError::Offline(cmds)) => {
        // Save for retry
        group.when().online().await;
        group.execute(cmds.into_iter().next().unwrap()).await?;
    }
    Err(CommandError::GroupTerminated) => {
        panic!("Group is gone");
    }
    Err(CommandError::NoCommands) => unreachable!(),
}
```

## Queries

Queries are read-only operations against the state machine. They are **not** replicated in the log.

### Weak Consistency

Reads from the local node's state machine. Fast but may return stale data:

```rust,ignore
let result = group.query(CounterQuery::Value, Consistency::Weak).await?;
println!("Local counter value: {} (at index {})", result.result, result.at_position);
```

### Strong Consistency

Forwards the query to the current leader, guaranteeing linearizable reads:

```rust,ignore
let result = group.query(CounterQuery::Value, Consistency::Strong).await?;
println!("Leader counter value: {} (at index {})", *result, result.state_position());
```

### CommittedQueryResult

Query results are wrapped in `CommittedQueryResult<M>`:

```rust,ignore
pub struct CommittedQueryResult<M: StateMachine> {
    pub result: M::QueryResult,    // The actual result
    pub at_position: Index,        // Log index at query time
}
```

It implements `Deref` to `M::QueryResult`, so you can use it directly:

```rust,ignore
let result = group.query(CounterQuery::Value, Consistency::Weak).await?;

// Deref to the inner result
let value: i64 = *result;

// Or access explicitly
let position = result.state_position();
let inner = result.into(); // Consume and get M::QueryResult
```

### Query Errors

```rust,ignore
pub enum QueryError<M: StateMachine> {
    Offline(M::Query),   // Node offline; query returned
    GroupTerminated,     // Group shut down
}
```

## Ordering Guarantee

Consecutive calls to `execute`, `execute_many`, `feed`, or `feed_many` on the same `Group` handle are guaranteed to be processed in the order they were issued.

## Common Patterns

### Command-Query Separation

```rust,ignore
// Write path: fire and forget for throughput
group.feed(OrderCmd::Place(order)).await?;

// Read path: weak consistency for speed
let book = group.query(OrderQuery::Snapshot, Consistency::Weak).await?;

// Read path: strong consistency for accuracy
let book = group.query(OrderQuery::Snapshot, Consistency::Strong).await?;
```

### Execute and Verify

```rust,ignore
let index = group.execute(CounterCmd::Increment(1)).await?;
let result = group.query(CounterQuery::Value, Consistency::Weak).await?;
assert!(result.at_position >= index);
```