Amadeus Protocol Node Storage Mechanisms: Mnesia KV, ETS, and RocksDB Architecture Explained

The Amadeus Protocol node uses a layered storage stack combining Mnesia KV for durable distributed state, ETS for in-memory caching, and optional RocksDB for large binary data, all abstracted behind a unified DB API.

The Amadeus Protocol node is an Elixir-based blockchain implementation that must balance durability, performance, and distributed consistency across its network participants. Its storage architecture reflects these trade-offs through a careful separation of concerns: persistent state survives restarts and propagates across nodes, ephemeral caches optimize hot-path reads, and pluggable backends accommodate different deployment scenarios. This article examines the storage mechanisms as implemented in the amadeusprotocol/node repository, with direct references to source file paths and function signatures.

Core Storage Mechanisms

The node organizes storage into three primary tiers, each serving distinct operational requirements.

Mnesia KV: Distributed Persistent Store

Mnesia KV is the foundational storage mechanism for data that must survive node restarts and maintain consistency across the distributed system. Built on Erlang's Mnesia database, it provides atomic transactions, table replication, and disc-based persistence with minimal operational overhead.

Mnesia KV tables are defined and accessed through dedicated modules that encapsulate schema details:

Table Module Purpose
ReplicaKV ex/lib/node/replica_gen.ex Tracks replica high-water marks for slashing prevention
NODEANR ex/lib/node/node_anr.ex Stores anti-replay nonces to prevent transaction replay attacks

The MnesiaKV module exposes a functional API for CRUD operations. Writes are synchronous and transactional, ensuring that critical state mutations—such as updating the last signed block height—are durably recorded before the operation returns.


# Update replica high-water mark in Mnesia KV

{:ok, _} =
  MnesiaKV.merge(
    ReplicaKV,
    "last_signed_height",
    %{height: new_height}
  )

On node startup, these tables are reloaded from disk, allowing the replica generator to resume from its last known position without external coordination. The node_anr.ex module similarly retrieves persisted anti-replay entries to validate incoming transactions:


# Retrieve anti-replay entry from Mnesia KV

case MnesiaKV.get(NODEANR, pk) do
  nil -> :not_found
  anr -> {:ok, anr}
end

ETS: In-Memory Runtime Caching

ETS (Erlang Term Storage) provides fast, read-optimized access to data that can be reconstructed from persistent storage or is transient by nature. Unlike Mnesia, ETS tables reside entirely in memory and are cleared on node restart, trading durability for speed and simplicity.

The ex/lib/node/node_state.ex module initializes ETS tables during the node supervision tree startup, populating them from Mnesia or computing values on demand. Typical ETS use cases include:

  • Cached peer connection lists
  • Recently processed block headers
  • Runtime configuration snapshots

ETS operations bypass Mnesia's transaction overhead, yielding microsecond-level read latencies for hot data paths.

RocksDB/LevelDB: Embedded Backend for Large Values

For deployments handling substantial binary payloads—such as contract state merkle trees or historical attestation proofs—the node supports an optional RocksDB or LevelDB backend. This embedded key-value store complements Mnesia by offloading large values that would degrade Mnesia's performance characteristics.

The backend selection is configuration-driven and transparent to calling code through the abstraction layer. When enabled, the following modules delegate to the embedded database:

Module Responsibility
ex/lib/api/db_chain.ex Block and header persistence
ex/lib/api/db_entry.ex General entry storage
ex/lib/api/db_attestation.ex Attestation record keeping

# Read through the generic DB API—backend selection is transparent

{:ok, entry} = DBAPI.get(:chain, key)

# Resolves to Mnesia or RocksDB based on :node configuration

Specialized Storage: Merkle Mountain Ranges

The Merkle Mountain Range (MMR) is a cryptographic accumulator structure used for compact proofs of chain history. Implemented in ex/lib/api/db_mmr.ex, it persists its internal nodes through the generic DB layer—meaning MMR data resides in whichever backend (Mnesia or RocksDB) is active.

This design allows the node to:

  • Generate inclusion proofs for any historical block without full chain traversal
  • Prune obsolete MMR peaks while retaining verification capability
  • Switch storage backends without invalidating accumulated proof data

Storage Abstraction and Pruning

All storage mechanisms converge through ex/lib/api/db_api.ex, which exposes a uniform CRUD interface regardless of the underlying implementation. This abstraction enables:

  • Development defaults: Pure Mnesia mode for local testing
  • Production scaling: Mixed mode with RocksDB for I/O-heavy workloads
  • Seamless migration: Backend swaps without application code changes

To prevent unbounded growth, the ex/lib/api/db_pruner.ex module implements time-based and height-based retention policies. The pruner operates against the generic DB API, ensuring consistent cleanup across Mnesia and RocksDB deployments:


# Execute pruning job—works with any configured backend

:ok = DBPruner.prune(:chain, older_than: :days_30)

Pruning is asynchronous and incremental, scanning and deleting entries in batches to minimize impact on consensus-critical operations.

Configuration and Operational Flexibility

The node's storage stack is not monolithic—operators can tune the trade-off between consistency, performance, and resource utilization through application configuration. The default development profile uses Mnesia exclusively, while production deployments may enable RocksDB for the chain and entry tables while retaining Mnesia for replica state and anti-replay data.

This flexibility is achieved without code duplication because all storage consumers interact through the DBAPI contract, with backend routing resolved at runtime based on table-specific configuration.

Summary

  • Mnesia KV provides durable, distributed, transactional storage for critical consensus state including replica high-water marks and anti-replay nonces
  • ETS delivers microsecond-latency in-memory caching for reconstructible or transient runtime data
  • RocksDB/LevelDB (optional) handles large binary payloads that would degrade Mnesia performance
  • Merkle Mountain Ranges implement cryptographic history proofs atop the generic DB layer
  • DB API abstraction unifies access patterns and enables backend portability
  • Pruning subsystem enforces retention policies across all storage backends

Frequently Asked Questions

What is Mnesia KV in the Amadeus Protocol node?

Mnesia KV is the node's primary persistent storage mechanism, built on Erlang's Mnesia distributed database. It provides atomic transactions, disc-based durability, and table replication across cluster nodes. The implementation in ex/lib/node/replica_gen.ex and ex/lib/node/node_anr.ex uses MnesiaKV.merge/3 and MnesiaKV.get/2 for synchronous, crash-safe state updates.

When does the node use ETS instead of Mnesia?

ETS is used for transient or reconstructible data where durability is unnecessary and speed is paramount. The ex/lib/node/node_state.ex module initializes ETS tables at startup for cached peer lists, recent headers, and runtime snapshots. Unlike Mnesia, ETS tables vanish on node restart and offer no cross-node replication.

Can the Amadeus Protocol node run without RocksDB?

Yes. RocksDB is strictly optional. The default configuration uses Mnesia for all storage needs, which is suitable for development and moderate-scale deployments. RocksDB is enabled through configuration to improve performance when storing large contract state or extensive historical data, but the DBAPI abstraction ensures identical behavior regardless of backend selection.

How does the node prevent storage from growing indefinitely?

The ex/lib/api/db_pruner.ex module implements automated pruning with configurable retention policies. It scans chain, entry, and attestation tables via the generic DBAPI, removing entries older than specified thresholds. Pruning runs asynchronously in batches to avoid blocking consensus operations, and works uniformly across Mnesia and RocksDB backends.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →