dstate 0.1.0

Distributed state replication across actor-based clusters
Documentation

dstate

Distributed state replication for Rust actor frameworks.

dstate provides a framework-agnostic core for managing replicated state across nodes in a cluster. State changes are projected into public views, synchronized via configurable strategies (active push, change-feed, periodic sync), and persisted through a pluggable storage interface.

Workspace Crates

Crate Description
dstate Core library — traits, types, replication logic, test support
dstate-ractor Adapter for the ractor actor framework
dstate-kameo Adapter for the kameo actor framework

Quick Start

Add the core crate and an adapter to your Cargo.toml:

[dependencies]

dstate = "0.1"

dstate-ractor = "0.1"   # or dstate-kameo = "0.1"

Define a Distributed State

Implement DistributedState for simple state types where the entire state is the public view:

use dstate::{DistributedState, DeserializeError};
use serde::{Serialize, Deserialize};

#[derive(Clone, Debug, Serialize, Deserialize)]
struct Counter {
    value: u64,
}

impl DistributedState for Counter {
    fn name() -> &'static str { "counter" }
    const WIRE_VERSION: u32 = 1;
    const STORAGE_VERSION: u32 = 1;

    fn serialize_state(&self) -> Vec<u8> {
        bincode::serialize(self).unwrap()
    }

    fn deserialize_state(bytes: &[u8], _version: u32) -> Result<Self, DeserializeError> {
        bincode::deserialize(bytes).map_err(|e| DeserializeError::Malformed(e.to_string()))
    }
}

For state types with private fields, delta synchronization, or view projections, implement DeltaDistributedState instead. See the example programs in each adapter crate's examples/ directory.

Use an Actor Runtime

use dstate::{ActorRuntime, ActorRef};
use dstate_ractor::RactorRuntime;  // or dstate_kameo::KameoRuntime

#[tokio::main]
async fn main() {
    let runtime = RactorRuntime::new();

    // Spawn an actor that handles u64 messages
    let actor = runtime.spawn("counter", |msg: u64| {
        println!("Received: {msg}");
    });

    // Fire-and-forget send
    actor.send(42).unwrap();
}

Architecture

┌─────────────────────────────────────────────┐
│              Application Code               │
├─────────────────────────────────────────────┤
│     dstate (core traits + replication)      │
│  ┌─────────┐  ┌──────────┐  ┌───────────┐  │
│  │  State   │  │  Sync    │  │ Persistence│  │
│  │  Traits  │  │ Strategy │  │  Traits    │  │
│  └─────────┘  └──────────┘  └───────────┘  │
├──────────────────┬──────────────────────────┤
│  dstate-ractor   │     dstate-kameo         │
│  (ractor adapter)│     (kameo adapter)      │
└──────────────────┴──────────────────────────┘

Key Concepts

  • State — The full internal data on each node (may contain private fields)
  • View — A public projection of state, shared with peers
  • ViewDelta — An incremental update to a view (optional, for bandwidth efficiency)
  • Generation(incarnation, age) pair for crdt-style conflict resolution
  • SyncStrategy — Configures how and when state changes are replicated:
    • active_push() — Push every mutation immediately
    • feed_lazy_pull() — Batch changes, pull on query
    • feed_with_periodic_sync() — Change feed plus periodic full sync
    • periodic_only() — Only periodic full snapshots
  • ActorRuntime — Framework-agnostic actor spawning, timers, and groups
  • ClusterEvents — Subscribe to node join/leave notifications
  • StatePersistence — Async save/load for crash recovery

Choosing an Adapter

Feature dstate-ractor dstate-kameo
Framework ractor kameo
Mailbox Unbounded Bounded (default)
Spawn Async (bridge thread) Sync (cheaper)
Send cast() fire-and-forget tell().try_send() fire-and-forget
Timers tokio tasks tokio tasks

Both adapters expose identical ActorRuntime semantics. Choose based on your preferred actor framework.

Testing

The core crate includes test_support with mock implementations:

  • TestRuntime — In-memory actor runtime with channel-based mailboxes
  • TestClock — Deterministic clock with manual advance()
  • InMemoryPersistence — In-memory save/load for testing
  • TestState / TestDeltaState — Example state implementations
use dstate::test_support::{TestRuntime, InMemoryPersistence};
use dstate::Clock;
use dstate::test_support::test_clock::TestClock;

Run all tests:

cargo test --workspace

License

MIT