actordb 0.9.0

An actor-first distributed database
docs.rs failed to build actordb-0.9.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: actordb-0.1.0

actordb

An actor-based distributed key/value database for embedded Rust applications, with logical tables, a standalone Rust SDK and opt-in persistence.

CI · crates.io · docs.rs · coverage

Current release and deployment

ActorDB 0.9.0 requires Rust 1.88+ and a fresh deployment. Deploy matching database, protocol, query and SDK crates together. Peers use exact protocol 6, application connections exact protocol 4, and RocksDB record/manifest format 3. There is no rolling wire upgrade, old-store adoption, compatibility API alias or automatic data conversion. Use new data directories and handle any source-data export/re-ingestion separately.

[dependencies]
actordb = "0.9.0"

The default feature set is empty. The Node uses a volatile actor cache and provides the KvStore interface without enabling a bundled store. RocksDB is an independent opt-in:

actordb = { version = "0.9.0", features = ["rocksdb"] }

The memory feature exposes a no-op test store, not durable storage. The tls feature provides a rustls transport configured with caller-provided certificates and trust policy. Custom transports are supported independently. Protocol bindings are committed Rust; normal builds require no protobuf compiler. RocksDB builds additionally need its native dependencies and libclang.

This is pre-1.0 software. See fresh deployment, release notes and publication procedure.

Embedded usage

use actordb::node::{DrainOptions, Node, NodeConfig};

let node = Node::start(
    "node-a".to_string(),
    "127.0.0.1:0".parse()?,
    NodeConfig::builder().fragment_count(1).build(),
).await?;

let users = node.table("users")?;
users.put("alice", b"hello".to_vec()).await?;
let value = users.get("alice").await?;
assert_eq!(value.unwrap().value, b"hello");

// Admission closes synchronously; accepted work and responses remain owned.
node.begin_drain();
node.drain_requests(DrainOptions::default()).await?;
node.finish_shutdown(DrainOptions::default()).await?;

The default table is KV; named table handles share the same Node, listener and peer connections while isolating keys, queries, caches and durable namespaces. Equal user keys hash to the same fragment across tables.

Point operations support puts, reads, deletes, TTL and priority. Batch APIs preserve input positions and per-key outcomes; they are not transactions. SQL names its relation explicitly and supports bounded filters, projections, pagination and exact aggregates over logical tables.

What the database provides

  • Actor execution: each fragment has a supervised dispatcher, table-local High/Default/Background worker pools, a TTL cleaner and a periodic snapshotter. Sticky worker routing preserves per-key ordering within a priority pool; shared-state LWW merging orders competing work across pools.
  • Placement and replication: static replica maps and caller-controlled dynamic placement, bounded forwarding, tracked batch ACKs, and streamed session/round-correlated anti-entropy. Late joiners and restarted nodes recover through reconciliation.
  • Versioned records: ordering by (timestamp_ms, logical_counter, node_id, is_tombstone). Deletes and expiry markers are retained tombstones; reads hide them, while replication and checkpoints preserve them.
  • Durable boundaries: configured or explicit stable local fences, cancellation-safe application drain, receive/apply shutdown and separate per-target replication-drain evidence.
  • Write concerns: local/count/majority and region requirements, strict cap enforcement, actual memory/store/stable evidence, frozen replica identities and operation/version-floor receipts.
  • Observations: owned startup, typed lifecycle/readiness/blockers, actual replication progress and bounded backend-neutral node/table/fragment/peer/ pool/worker metrics with optional timing and explicit freshness/omissions.
  • Recovery and membership: stable bounded checkpoints, authenticated chunks, resumable atomic import cursors, stable target-floor certificates and guarded joint/final placement workflows.

Transport and identity

Plain TCP is useful for local testing. Real deployments should supply mutually authenticated TLS through Network or the optional TlsNetwork. ActorDB does not provision certificates or choose the host's trust policy.

NodeConfig::cluster_secret optionally supplies at least 32 high-entropy random bytes. Both peers prove fresh possession through a challenge-bound HMAC. There is no capability or version-range negotiation: a non-current peer is rejected. The shared secret proves possession, not which member is speaking, and does not encrypt or authenticate subsequent frame contents. TLS supplies those properties.

Receiver generation allowlists and hot fences close old sessions and retain already-accepted actor, response and store work until completion. They are operational controls, not proof of credential revocation. A process retaining valid TLS/shared-secret credentials can claim an allowed generation; excluding it requires external credential revocation and application writer authority. There are no Ed25519 process keys or signed-frame headers.

Rust application SDK

The peer listener is not an application endpoint. Bind a client_server::ClientServer around an Arc<Node> and connect using actordb-client. Application connections never become replication members.

actordb-client = "0.9.0"

The SDK supports point and batch operations, TTL, priorities, write concerns, logical table views, bounded scans/SQL and read-only local inspection. Clones share one serialized connection. Count/byte admission and total request deadlines are bounded.

Application credentials are independent of the peer cluster secret. Client::from_stream, ClientServer::bind_with_network and from_acceptor accept caller-authenticated TLS transports. Closing the SDK does not shut down the database. Mutations are not automatically replayed after uncertain delivery.

Persistence and checkpoints

Without a store, a Node restart loses its local cache. Use NodeConfig::with_rocksdb or with_rocksdb_options for offered persistence, or provide an explicit custom KvStore. RocksDB defaults to synchronous WAL writes, validates its current identity/layout, and rejects non-current stores without adoption or repair.

Request drain defaults to the configured local persistence contract. DrainDurability::Applied waits for execution/response ownership only; StableCommitted requests an explicit stable backend fence. Timeout or cancellation leaves work owned and admission closed for retry.

Completed shutdown releases the Node's store attachment. Keep a caller-owned backend Arc when exporting a checkpoint after shutdown; an expired weak handle returns BackendReleased. Export uses a stable bounded snapshot. Import verifies chunks before applying them and commits records/cursor atomically. An incomplete import blocks ordinary access until exact retry completes.

Checkpoint format is ACTCP003; staged membership journals use ACTMEM04. Native certificates check actual receiver fence/drain reports and retained stable target floors. Neither these certificates nor a Serving snapshot establishes application writer authority.

Public crate boundaries

  • actordb::node: Node/table operations, runtime configuration, drain, status and metrics.
  • actordb::actor: dispatcher/supervisor/maintenance APIs and shared fragment state. Managed worker pools remain internal.
  • actordb::replication: bounded standalone sender construction and replication reports; no unbounded compatibility entry point.
  • actordb::store, checkpoint, membership, placement, network, query, write_concern and ttl_expiry: their respective support contracts.
  • actordb-protocol: public actordb_protocol::meta and application wire schemas, current protocol constants and portable write-evidence types.
  • actordb-query: portable SQL/query planning without database/native dependencies.
  • actordb-client: standalone application SDK.

There is no public actordb::proto or actordb::supervision compatibility module. NodeStatus.lifecycle is the typed lifecycle, drain is the separate ownership snapshot and shutdown_stage is always present. Unimplemented percentage/lag fields and no-op tombstone collection are absent.

Documentation

Validation and examples

cargo test --workspace --exclude docker-tests
cargo test --workspace --exclude docker-tests --features actordb/memory
cargo test --workspace --all-features
cargo clippy --workspace --all-features --all-targets -- -D clippy::all -D warnings

Examples include three_node_demo, lifecycle_observations, peer_fence, write_concern_evidence, checkpoint_restore and staged_membership. Native checkpoint/membership examples require --features rocksdb and fresh directories. The SDK's tls_demo runs a caller-configured mTLS mesh and application listener.

Docker scenarios exercise three-node replication, SIGKILL/restart reconvergence and a separate SDK-only client across sharded nodes and ingress restart. See testing for commands and CI coverage.

Limits

ActorDB is not consensus or a transaction system. It does not elect writers, allocate hosts, prove host death, or infer global equality from a queue/ACK. Worker crashes can abandon accepted mailbox jobs; orderly shutdown drains them. Backpressure is explicit, but actor queues and wire budgets are not a process-wide heap bound. Unknown outcomes remain unknown.

Contributing and license

See CONTRIBUTING.md. Licensed under MIT.