Fact-checked by Grok 2 weeks ago

Mnesia

Mnesia is a distributed, soft management system (DBMS) written in Erlang and integrated into the Erlang/OTP platform, specifically designed for industrial-grade applications requiring high , fast operations, and dynamic reconfiguration. It serves as a multiuser, distributed DBMS that runs in the same as Erlang applications, providing a tight between the database and the programming to eliminate impedance mismatches common in traditional database systems. Data in Mnesia is organized into tables, each identified by a unique atom name and composed of user-defined Erlang records, supporting a hybrid relational/object data model with configurable types such as set (unique keys), bag (multiple records per key), or ordered_set. Tables can be replicated across nodes for fault tolerance, with storage options including ram_copies (in-memory only), disc_copies (both memory and disk), or disc_only_copies (disk only), enabling location transparency and distributed transactions that span multiple nodes atomically. Mnesia supports dynamic schema reconfiguration at runtime, allowing tables to be created, modified, or moved without downtime, which is particularly suited for nonstop, high-availability systems in telecommunications. Key operations in Mnesia include atomic s via functions like mnesia:transaction/1, which ensure consistency across distributed reads, writes, and deletes, as well as "dirty" operations for higher performance in soft scenarios at the risk of temporary inconsistencies. It incorporates the (QLC) for efficient, complex queries expressed in Erlang syntax, supporting joins, selections, and aggregations over replicated data. While optimized for replicated, telecom-oriented workloads with fast lookups and event handling, Mnesia is not intended for hard constraints or large-scale processing, instead excelling in environments demanding rapid reconfiguration and fault recovery.

Overview

Introduction

Mnesia is a distributed, soft real-time, multiuser database management system (DBMS) written in Erlang, designed for telecommunications and other high-availability applications. It provides fault-tolerant data management that eliminates the impedance mismatch between the database and the Erlang programming language, enabling seamless integration for industrial-grade systems. The core design goals of Mnesia emphasize , , in distributed environments, and tight coupling with Erlang's model for concurrency. It supports relational-like tables with configurable replication and persistence, along with transactions and mechanisms for complex queries, facilitating fast real-time lookups and dynamic reconfiguration without downtime. As a built-in application within the Erlang/OTP platform, Mnesia functions as a persistent, distributed solution optimized for soft , high-availability scenarios in clustered deployments. Originating from Ericsson's requirements, it has evolved to support robust data handling in fault-prone, scalable networks.

History and Development

Mnesia was developed by the Laboratory in the early 1990s as a management system tailored for high-availability applications, addressing the need for real-time data management in concurrent, fault-tolerant systems built with Erlang. Designed to integrate seamlessly with Erlang's concurrency model, Mnesia emerged from 's efforts to create robust tools for telecom infrastructure, where downtime and data consistency were critical challenges. The initial public release of Mnesia occurred in 1996 as a core component of the Open Telecom Platform (OTP), Ericsson's framework for building scalable Erlang applications, marking its integration with underlying storage mechanisms like ETS (Erlang Term Storage) for in-memory operations and DETS (Disk ETS) for persistent storage to enable hybrid in-memory and disk-based functionality. Initially proprietary software within Ericsson's ecosystem, Mnesia was open-sourced alongside Erlang in December 1998 under the Erlang Public License (EPL), a derivative of the , to foster broader community adoption and contributions. Over subsequent years, Mnesia evolved through iterative enhancements in OTP releases, with key improvements to replication protocols for better and support for "dirty" operations to optimize performance in high-throughput scenarios, as seen in updates across OTP versions from R9 (2003) onward. A significant milestone came in 2015 with OTP 18, when the entire Erlang/OTP platform, including Mnesia, transitioned to the 2.0, promoting wider commercial use and compatibility with other open-source projects by removing EPL's restrictions. This licensing shift, announced in March 2015, facilitated Mnesia's integration into diverse non-telecom applications while maintaining its core focus on distributed reliability. Mnesia's development and maintenance have been led by Ericsson's Erlang/OTP , with ongoing contributions ensuring and enhancements up to OTP 27 (released in June 2024) and OTP 28 (released in May 2025, including Mnesia version 4.24 with support for EEP-69 nominal types for improved type systems), reflecting its enduring role in the platform's evolution for real-time, needs.

Architecture

Data Model

Mnesia employs an extended relational data model where data is organized into tables composed of Erlang records, which are essentially tagged tuples. Each record represents a row in the table, with the first element being the table name (an atom) and the second element serving as the primary key, forming a unique object identifier (OID) for the record. For instance, a record might appear as {employee, 104732, "klacke", 7, male, 98108, {221, 015}}, where employee is the table name and 104732 is the primary key. This structure allows Mnesia to store arbitrary Erlang terms, including complex nested structures like lists or tuples, rather than being limited to primitive data types found in traditional relational databases. Tables in Mnesia support three primary types: set, bag, and ordered_set. A set table enforces uniqueness, permitting only one record per primary key, with insertions overwriting existing records sharing the same key; this is the default type. In contrast, a bag table allows multiple records per key, enabling duplicates while ensuring individual records remain unique. The ordered_set type maintains one record per key but stores them sorted by the key value, facilitating range queries, though it is incompatible with certain persistent storage options. Primary keys are defined as the first attribute in the record definition, and secondary indexing can be applied to non-key attributes to optimize lookups, specified via the {index, [AttributeName]} option during table creation. For example, indexing on an address field would use {index, [address]}. While Mnesia incorporates relational concepts, it diverges from traditional RDBMS by lacking explicit foreign key support and full SQL semantics; instead, relationships are modeled through application logic or additional reference tables, and joins are performed using match specifications in functions like mnesia:select/3. These match specifications, written as Erlang terms, allow pattern matching and filtering akin to lightweight queries, such as [{#person.age= '$1', _ = '_'}, [{' > ', '$1', 30}], ['$1']}] to select ages greater than 30. Schema enforcement is not strict at runtime; attributes are declared at table creation using {attributes, [AtomList]}—often derived from record_info(fields, RecordName)—but dynamic handling permits flexibility without rigid validation, emphasizing Mnesia's suitability for distributed, real-time Erlang applications. Table schemas are defined programmatically using mnesia:create_table/2, which specifies the logical structure including type, attributes, and indexes. A representative example for an employee table might be:
erlang
mnesia:create_table(employee,
    [{type, set},
     {attributes, record_info(fields, employee)},
     {index, [department]}]).
Here, employee is a set table with attributes matching the employee record fields (e.g., {id, name, department, salary}), and an index on the department attribute for efficient secondary lookups. This approach integrates seamlessly with Erlang's record system, promoting type safety at compile time while allowing runtime adaptability.

Storage and Replication

Mnesia supports three primary backend types for s, each balancing , , and resource usage differently. The ram_copies type stores exclusively in on the specified nodes, offering the highest for read and write operations due to the absence of disk I/O, but it is volatile and loses all upon node crash unless replicated to persistent copies on other nodes. In contrast, disc_copies maintains both in and on disk across the designated nodes, ensuring through transactional to disk, though this introduces overhead from synchronous disk writes that can reduce throughput compared to pure in-memory . The disc_only_copies type stores solely on disk, providing without consuming for the itself, but it incurs the highest for operations due to direct disk access without caching, making it suitable for archival or low-access scenarios. Replication in Mnesia allows tables to be copied across multiple nodes, with the storage type specified independently for each to enable hybrid configurations, such as combining ram_copies for speed on active nodes with disc_copies for durability elsewhere. Replication operates in synchronous or asynchronous modes depending on the access context: full transactions ensure synchronous propagation using a , guaranteeing atomic across all replicas before committing, while sync_dirty operations perform synchronous updates to all active replicas without full locking, and async_dirty operations apply changes locally first with asynchronous replication to other nodes, prioritizing availability over immediate . The , which defines table structures and node memberships, is automatically replicated to all disk-resident nodes using ram_copies or disc_copies , ensuring cluster-wide awareness during startup or modifications. In the event of node failures, Mnesia's replication model maintains availability if a majority of replicas remain operational, particularly for synchronous transactions that require quorum for commits, though asynchronous modes allow continued local operations with potential for eventual consistency recovery upon node reconnection. For custom storage integration, Mnesia supports an experimental external copies feature via the {external_copies, [Nodes]} option in mnesia:create_table/2, combined with {storage_properties, [{BackendModule, Props}]} where BackendModule implements the mnesia_external_copy behavior; backend modules (e.g., for RocksDB via mnesia_rocksdb:register/0) handle operations and enable high-performance key-value persistence without mixing with native storage types like ram_copies, disc_copies, or disc_only_copies. This API was introduced experimentally in OTP 19 (2016) and remains experimental as of OTP 27. Data durability is enhanced through mechanisms like checkpointing, which captures a transactionally consistent snapshot of multiple tables for backup while allowing ongoing access, though it increases memory usage and may slow updates on retainer nodes. For ram_copies tables, periodic dumping via mnesia:dump_tables/1 saves content to .DCD files on disk with minimal disruption, enabling reload at startup to mitigate volatility. Backups can be performed online using mnesia:backup_checkpoint/2 on an active checkpoint, supporting custom modules for export formats, while loading functions like mnesia:force_load_table/1 ensure table availability post-restart, albeit with risks of inconsistency if replicas are unavailable; overall, persistent storage types like disc_copies and disc_only_copies provide higher availability and faster recovery compared to volatile ram_copies, at the cost of performance.

Distribution and Clustering

Mnesia operates as a management system by leveraging the Erlang runtime's built-in mechanism, which enables transparent communication and across multiple nodes in a . This integration allows processes on different nodes to interact as if they were local, facilitating replication and coordination without explicit location awareness in application code. The system's design ensures that tables can be replicated across nodes, providing and in fault-tolerant environments. Clustering in Mnesia begins with the creation of a shared using the mnesia:create_schema/1 , which establishes a replicated system on designated disc-resident nodes, each requiring a unique directory for . Nodes join the by starting the Mnesia application via application:start(mnesia) or mnesia:start(), after which they synchronize information with existing members. The parameter extra_db_nodes can specify additional nodes to connect to beyond those defined in the , enabling flexible expansion. Full replication distributes all s across all nodes, while partial replication allows selective copies, with replication types such as ram_copies, disc_copies, or disc_only_copies determining behavior. Synchronization occurs in two phases: initial table loading upon node joining and ongoing updates through distributed transactions. When a node joins, it loads replicas from other s based on the load_order attribute ( 0, with higher values loading first), using parallel loaders controlled by the no_table_loaders ( 2). Ongoing is maintained via transactional s, where updates are logged and replicated to all relevant replicas synchronously or asynchronously, depending on the type. Enabling checking with {majority, true} in mnesia:create_table/2 ensures that transactions commit only if a of replicas acknowledge the update, enhancing reliability in partial scenarios. Fault tolerance is achieved through monitoring, automatic mechanisms, and strategies for partitions. Nodes monitor membership using mnesia:subscribe/1 to receive events such as node up/down or table changes, with debug levels adjustable via mnesia:set_debug_level/1 for . Automatic is supported by designating master nodes with mnesia:set_master_nodes/2, allowing recovering nodes to load tables from these masters during restarts. partitions are managed by the max_wait_for_decision (default ), which controls timeouts, though prolonged partitions may require manual intervention like mnesia:force_load_table/1 to resolve inconsistencies. Erlang's further aids by detecting node failures and reconnecting, with parameters like net_ticktime influencing intervals for partition detection. The distributed is managed across nodes, with the schema_location parameter (options: disc, [ram](/page/Ram), opt_disc; default opt_disc) dictating its . mismatches during startup are resolved by Mnesia attempting to merge definitions from nodes, or through fallback using mnesia:install_fallback/2 to recover from prior states. Deletion of the via mnesia:delete_schema/1 is irreversible and erases all associated data, necessitating careful use in production s. This -centric approach ensures uniform structures across the distributed .

Core Features

Transactions and Concurrency

Mnesia employs a transaction model that ensures and in distributed environments, adhering to the properties: atomicity, , , and durability. Full , initiated via mnesia:transaction/1 or mnesia:transaction/2, execute a series of database operations as a single atomic unit, with all changes committed across all replicas or rolled back entirely upon failure. This mechanism uses a to coordinate updates spanning multiple tables and nodes, guaranteeing that either all nodes apply the changes or none do, even in the presence of network partitions or node failures. For scenarios requiring higher performance at the cost of strict guarantees, Mnesia provides dirty operations such as mnesia:dirty_read/1, mnesia:dirty_write/1, and mnesia:dirty_delete/1, which transaction locking and commit directly to the local storage without replication or capabilities. These operations are non-atomic and do not ensure , making them suitable for read-heavy or low-contention workloads where speed is prioritized over full consistency. To balance efficiency and control, developers can use mnesia:activity/4 for batched operations in contexts like async_dirty or sync_dirty, enabling asynchronous updates for high throughput while optionally synchronizing across nodes. Concurrency in Mnesia is managed through a sophisticated locking system that employs read (shared) locks for readers and write (exclusive) locks for writers on individual records or entire tables, preventing data races and ensuring serializable by default—meaning concurrent transactions appear to execute sequentially without interference. Deadlocks are detected and resolved using a "wait-die" strategy, where transactions are assigned timestamps; younger transactions yield (wait) or abort (die) based on priority, with automatic up to a configurable limit to minimize disruption. In soft systems, such as applications, long-running transactions are mitigated by configurable lock acquisition timeouts and the encouragement of short transactions or dirty operations to avoid blocking other processes, though full transactions may abort and if locks cannot be acquired promptly.

Queries and Indexing

Mnesia provides flexible query mechanisms primarily through and iteration functions, enabling efficient data retrieval from distributed s without native SQL support. Queries are typically performed within transactions to ensure , though the focus here is on the retrieval patterns themselves. The core query function, mnesia:select/3, uses Erlang Term Storage () match specifications—lists of tuples defining patterns, guards, and result bindings—to filter records in a specified , returning a list of matching objects up to a configurable limit. For example, to retrieve names of individuals aged exactly 36 from a person , a match specification might be [{#person{name='$1', age=36, _='_'}, [], ['$1']}], where '&#36;1' binds to the name field and _ ignores others. For aggregating or processing entire tables, Mnesia offers folding functions like mnesia:foldl/4, which applies a user-defined to each sequentially, accumulating results while supporting read or write locks to control concurrency. This is particularly useful for computations such as summing values or building derived data structures, iterating from the table's start to end. A reverse-order variant, mnesia:foldr/4, is available for ordered sets to process from end to start. Additionally, mnesia:match_object/3 performs similar to select/3 but without custom result bindings, directly returning full records that match a partial like {person, '_', 36, '_', '_'}. Indexing in Mnesia enhances query performance by avoiding full table scans, which can be costly in large or distributed environments. Every table automatically includes a index on the first attribute (or specified key position), facilitating fast lookups via functions like mnesia:read/2 or mnesia:read/3. For non-key attributes, secondary indexes can be added using mnesia:index/2 or declared during table creation with the {index, [AttrList]} option, where AttrList specifies attribute names or positions (e.g., [age] or [2]). Once indexed, queries leverage these via specialized functions: mnesia:index_read/3 retrieves records by indexed value (e.g., mnesia:index_read(person, 36, #person.age)), while mnesia:index_match_object/4 combines with index traversal for bounded elements. Removing an index is done with mnesia:del_table_index/2, though this requires careful consideration of ongoing queries. Indexes increase storage overhead and slow insertions but significantly speed up selective retrievals. For more complex, relational-like operations such as joins across tables, Mnesia relies on multiple sequential selects or dirty operations like mnesia:dirty_match_object/1 for non-transactional efficiency, though these lack atomicity guarantees. A higher-level abstraction is provided through the Query List Comprehension (QLC) module in the Erlang standard library, which integrates seamlessly with Mnesia tables via mnesia:table/1 or mnesia:table/2. QLC allows SQL-inspired list comprehensions, such as qlc:q([P || P <- mnesia:table(person), P#person.age > 30, Q <- mnesia:table(query), Q#query.id =:= P#person.id]), to perform joins and filters while automatically optimizing for indexes when possible—using select-based traversal over raw scans. Options in mnesia:table/2, like {traverse, {select, MatchSpec}} or {n_objects, N}, further tune QLC for chunked reads or custom matching, reducing memory and network load in clustered setups. Performance in Mnesia queries benefits substantially from proper indexing, as unindexed select/3 or match_object/3 defaults to scanning all records, potentially leading to linear time complexity in table size. Guarded match specifications, such as [{'==', '&#36;1', 42}] in a tuple like [{#example{key='&#36;1', _='_'}, [{'==', '$1', 42}], ['$_']}], allow complex conditions (e.g., equality, ranges) evaluated efficiently when combined with indexes, minimizing data transfer across nodes. QLC further aids optimization by compiling comprehensions into indexed selects where feasible, though users must verify index usage via mnesia:table_info/2 to ensure scalability.

Schema and Table Management

Mnesia requires the creation of a schema prior to defining any tables, as the schema serves as the foundational metadata repository for the entire database structure. The primary function for schema initialization is mnesia:create_schema/1, which accepts a list of nodes to establish the schema across, ensuring a unique Mnesia directory per node on local disc devices. This process fails if the specified nodes are not alive, if Mnesia is already running, or if a schema already exists on those nodes. In distributed environments, mnesia:create_schema/1 facilitates a shared schema among multiple nodes, promoting consistency in table definitions and replication strategies. For single-node setups, a local schema is created by passing only the current node, while disc-less nodes automatically generate a minimal in-memory default schema upon Mnesia startup without explicit creation. Only disc-resident nodes should be included in the schema list to avoid errors, and existing schemas can be removed using mnesia:delete_schema/1 if persistent data obsolescence is acceptable. Table management begins with mnesia:create_table/2, which takes a table name (an atom) and a list of options to configure properties such as structure, storage, and behavior. Essential options include {attributes, [atom()]} to define record fields, {type, set | ordered_set | bag} to specify uniqueness and ordering semantics (with ordered_set unsupported for disc-only copies), and replica directives like {ram_copies, [node()]} for in-memory storage, {disc_copies, [node()]} for dual RAM-disc persistence with logging, or {disc_only_copies, [node()]} for slower disc-only access. The {local_content, boolean()} option (default: false) allows tables with shared names but node-local contents, useful for non-replicated data. Additional configuration options in mnesia:create_table/2 encompass {majority, boolean()} (default: false) to enforce update acknowledgments from a majority of replicas for enhanced consistency, {index, [atom()]} for secondary attribute indexes that trade space and insertion time for query efficiency, and {load_order, non_neg_integer()} (default: 0) to prioritize table loading during Mnesia startup. Storage can be further tuned via {storage_properties, [{Backend, [Prop]}]} where Backend can be ets, dets, or an external backend (introduced in OTP 27), passing backend-specific properties to optimize performance. Since OTP 27, Mnesia supports external storage backends, enabling integration with custom or third-party storage engines such as for improved scalability. Other settings include {access_mode, read_write | read_only} (default: read_write), {record_name, atom()} for custom record tags, and {snmp, term()} for monitoring integration. Replication-related options, such as copy types and majority, are detailed further in the storage and replication mechanisms. Tables can be altered post-creation using functions like mnesia:add_table_index/2, which adds an index on a specified attribute to support faster lookups at the cost of increased storage and write overhead. For changing storage types, mnesia:change_table_copy_type/3 converts replicas (e.g., from ram_copies to disc_copies) while maintaining data integrity. Access modes are adjusted with mnesia:change_table_access_mode/2, load orders via mnesia:change_table_load_order/2, and majority settings through mnesia:change_table_majority/2. Indexes can be removed using mnesia:del_table_index/2. The lifecycle of tables includes automatic loading at Mnesia startup based on load_order, with higher values loaded first to ensure dependencies are met. To delete a table entirely, mnesia:delete_table/1 removes all replicas and metadata transactionally, rendering the table inaccessible. For emptying contents without deletion, mnesia:clear_table/1 removes all records. Replicas are added with mnesia:add_table_copy/3 or moved via mnesia:move_table_copy/3, and removed using mnesia:del_table_copy/2. Schema evolution, such as adapting to changing record formats, is handled by mnesia:transform_table/3 or mnesia:transform_table/4, which apply a user-defined transformation function to migrate data while updating attributes and optionally record names. In cases of network partitions or restarts, mnesia:set_master_nodes/2 designates load sources, and mnesia:force_load_table/1 overrides default loading for availability, though at the risk of temporary inconsistencies. Backups for recovery are installed via mnesia:install_fallback/2. Potential inconsistencies in schema or table states can be diagnosed using mnesia:info/0, which outputs details on active tables, transactions, locks, and memory usage to identify and resolve issues.

Implementation in Erlang

Basic Usage

To begin using Mnesia, the database must first be started on the Erlang node, typically after specifying a directory for storage via the -mnesia dir command-line option. The function mnesia:start() initiates the local Mnesia system asynchronously, returning ok upon launch, while application:start(mnesia) provides an equivalent method integrated with the OTP application framework. For a new installation, a schema must be created before starting, using mnesia:create_schema([node()]) to initialize an empty database structure on the current node; this returns ok on success or {error, Reason} on failure, such as if Mnesia is already running or the schema exists. Once started, tables can be created programmatically with mnesia:create_table/2, specifying options such as {ram_copies, [node()]} for in-memory storage or {attributes, record_info(fields, person)} to define fields based on an Erlang record. Basic data operations in Mnesia revolve around create, read, update, and delete (CRUD) actions, which must be wrapped in transactions for atomicity and consistency. Inserts and updates use mnesia:write/1, passing an Erlang record or tuple; for example, to add a person record:
erlang
mnesia:transaction(fun() -> 
    mnesia:write(#person{id = 1, name = "Alice", age = 30}) 
end).
This returns {atomic, ok} on success. Reads employ mnesia:read/2 or mnesia:read/3 (the latter allowing a lock kind like write), returning a list of matching records or an empty list if none found; for instance:
erlang
mnesia:transaction(fun() -> 
    mnesia:read(person, [1](/page/1), read) 
end).
Deletions are handled by mnesia:delete/1 or mnesia:delete/2, specifying the table and key:
erlang
mnesia:transaction(fun() -> 
    mnesia:delete({person, [1](/page/1)}) 
end).
This also returns {atomic, [ok](/page/OK)} if successful. Updates follow the same pattern as inserts: read the record, modify it, and write it back within a . Error handling in basic usage focuses on transaction outcomes, which return {aborted, Reason} for failures such as {no_exists, Table} when a table is not found or {timeout, Tables} for lock timeouts during concurrent access. Developers should check these results and retry s if needed, using mnesia:wait_for_tables/2 after startup to ensure tables are accessible within a specified timeout (e.g., 20 seconds). For reliable startup in production, Mnesia integrates with OTP supervision trees by declaring it as a required application in the .app file and starting it via the supervisor's boot process, ensuring it restarts on failure as part of the overall system fault tolerance. This approach leverages OTP's supervision principles to maintain database availability.

Advanced Operations

Mnesia provides several mechanisms for performing high-performance operations that bypass the full transactional overhead, known as dirty operations. These are particularly useful in scenarios requiring low-latency access, such as real-time systems, where the trade-off of potential inconsistency is acceptable. The mnesia:dirty_write/1 function writes a record directly to a table without initiating a transaction, ensuring the operation is atomic at the local node but not guaranteed to be consistent across a distributed cluster without additional synchronization. Similarly, mnesia:dirty_read/2 retrieves all records matching a given key from a specified table without locking or transaction context, returning a list of tuples or an empty list if no matches exist. For deletions, mnesia:dirty_delete/2 removes records associated with a key from a table in a non-transactional manner, which can improve throughput but risks leaving the database in an inconsistent state if the node fails mid-operation. To mitigate durability issues with these operations, mnesia:dirty_sync/0 forces an immediate synchronization of the transaction log to disk on the local node, ensuring that all pending dirty updates are persisted before proceeding, though it introduces a brief performance pause. For efficient traversal and processing of large datasets, Mnesia supports folding and asynchronous operations. The mnesia:foldl/4 applies a user-defined folding to each in a table, accumulating results from left to right while acquiring a specified lock kind (such as or write) to prevent concurrent modifications during . This is ideal for computations, like summing values or collecting statistics, without loading the entire table into memory. Complementing this, asynchronous dirty operations via mnesia:async_dirty/1 or mnesia:async_dirty/2 allow execution of arbitrary in the background without protection, logging changes for replication but forgoing locks to enable parallelism; the receives arguments and returns a result, making it suitable for non-critical background tasks like periodic cleanups. Performance tuning in Mnesia involves configuring and parameters to reliability and efficiency. dumps can be enabled by setting the -mnesia core_dir [Directory](/page/Directory) option at startup, directing crash dumps to a specified directory for post-mortem analysis, which is disabled by default to conserve resources. properties are adjustable via command-line flags, such as -mnesia dc_dump_limit Number (defaulting to 4), which controls the frequency of automatic dumps to disk based on growth, preventing unbounded expansion; related thresholds like the dump write threshold can be queried via mnesia:system_info(dump_log_write_threshold) to write accumulation before dumping. tuning is achieved through mnesia:set_debug_level/1, which sets verbosity levels from none (default, minimal output) to trace for detailed event , aiding in distributed inconsistencies without impacting performance. Backup and restore operations in Mnesia facilitate for large-scale deployments. The mnesia:backup/1 creates a checkpoint-based of all tables to a destination (e.g., a file), ensuring maximum redundancy by including and information, and supports custom modules for tailored formats. For restoration, mnesia:restore/2 loads data from a source , applying options like {clear_tables, [TableList]} to overwrite existing tables or {skip_tables, [TableList]} to preserve them, which is crucial for handling large datasets by minimizing ; for extremely large backups, the mnesia:traverse_backup/6 utility iterates incrementally, applying transformations to avoid memory exhaustion. Event-driven interactions are supported through mnesia:subscribe/1, which allows processes to receive notifications for changes to specific tables. Subscriptions can be for simple events (e.g., {table, Tab, simple}) that notify on inserts, updates, or deletes without full record details, or detailed ones including the affected records, delivering messages like {mnesia_table_event, {insert, Tab, Record}} to the subscribed process for reactive behaviors such as caching updates or auditing. This mechanism integrates seamlessly with Erlang's , enabling decoupled, scalable event handling across clustered nodes.

Applications and Use Cases

Notable Deployments

Mnesia has been widely adopted in messaging and real-time communication systems due to its distributed nature and integration with Erlang. One prominent example is , an open-source XMPP server, which utilizes Mnesia as its default backend for storing user sessions, rosters, and offline messages. This setup allows to handle persistent data across clustered nodes efficiently, supporting high-availability chat services in deployments ranging from small teams to large-scale environments. In the messaging broker domain, 's early versions relied on Mnesia to manage metadata such as queues, exchanges, bindings, users, and virtual hosts. This choice leveraged Mnesia's in-memory performance and replication for coordinating distributed message routing, though scalability challenges with large datasets prompted a migration to the store in 4.0 and later releases. Mnesia's origins in make it a staple in Ericsson's systems, where it supports call in operations support systems () and switching platforms like derivatives of the AXE series. Designed specifically for industrial-grade applications, Mnesia enables fault-tolerant and replication of dynamic , ensuring low-latency operations in high-volume environments. Early implementations of 's backend also incorporated Mnesia for handling user data, routing tables, and offline message queues, capitalizing on its embedded efficiency within the Erlang to scale to millions of concurrent users. This allowed WhatsApp to process transient data without external database dependencies, though custom modifications like asynchronous dirty transactions were employed to optimize for massive scale. In , Klarna's KRED system, which tracks and servicing operations, was initially powered by Mnesia starting around 2004, benefiting from its distributed transactions for updates. However, as data volumes grew to hundreds of gigabytes, limits led to a zero-downtime to in 2022, highlighting Mnesia's strengths in early-stage, low-to-medium scale deployments. Beyond these, Mnesia finds use in custom and telecom applications, such as EMQX, an broker for IoT messaging, which extends Mnesia with the Mria protocol to support clusters handling over 100 million connections for device . Its lightweight, embedded design makes it suitable for scenarios requiring distributed state management without heavy infrastructure.

Advantages and Limitations

Mnesia provides seamless integration with the Erlang runtime, utilizing Erlang records for data representation and leveraging the language's actor-based concurrency model to treat the database as an extension of the application itself, which eliminates impedance mismatches common in external database integrations. Its built-in distribution allows tables to be replicated across nodes using storage types such as ram_copies for in-memory replication or disc_copies for persistent replication, enabling fault-tolerant operations without additional . Designed for soft telecommunications applications, Mnesia delivers low-latency data access through dirty operations that bypass full overhead, achieving higher efficiency than transactional reads for high-throughput scenarios. Replication is facilitated via atomic s with properties—atomicity, consistency, isolation, and durability—ensuring high by propagating changes synchronously or asynchronously across replicas while supporting majority quorum for critical data. Despite these strengths, Mnesia has notable limitations in and functionality. It is not optimized for massive datasets, with disc_only_copies tables capped at 2 each due to underlying DETS storage constraints, and disc_copies limited by available since the entire table must load into on startup; practical recommendations suggest keeping tables under 1-10 to avoid degradation. Mnesia lacks full SQL support, instead relying on Query List Comprehensions (QLC) for querying, which offers Erlang-specific list-based operations but falls short in ad-hoc querying, joins, and complex analytics compared to relational standards. Additionally, it enforces a single per cluster, stored as a special table with a unique for node authentication, which restricts multi-schema deployments and requires careful planning for schema evolution in distributed environments. In comparisons, Mnesia outperforms key-value stores like in relational capabilities and compliance, supporting sets, ordered sets, and bags with low-latency microsecond reads for co-located data, but provides superior scalability for key-value workloads, handling hundreds of nodes with automatic rebalancing and higher distributed throughput. Against , Mnesia achieves faster latency for small, distributed in-memory datasets due to its nature and avoidance of round-trips, making it for caches or configuration stores, though 's mature SQL engine better handles complex queries and larger-scale persistence. Performance-wise, Mnesia excels in concurrent reads and writes via and lightweight processes, enabling efficient isolation without lost updates, but it struggles with intricate analytical workloads where QLC's limitations hinder optimization and index utilization.

References

  1. [1]
    Overview — mnesia v4.24.1 - Erlang
    Mnesia is a fault-tolerant, multiuser distributed DBMS for telecommunications, designed for Erlang, with fast real-time lookups and dynamic reconfiguration.
  2. [2]
    mnesia v4.24.1 - Erlang/OTP
    Data in Mnesia is organized as a set of tables. Each table has a name that must be an atom. Each table is made up of Erlang records. The user is responsible for ...
  3. [3]
    Open Telecom Platform - Wikipedia
    The OTP distribution is supported and maintained by the OTP product unit at Ericsson, who released Erlang/OTP as open-source in the late 1990s, to ensure its ...
  4. [4]
    Mnesia Release Notes
    This document describes the changes made to the Mnesia system from version to version. The intention of this document is to list all incompatibilities as well ...Missing: initial | Show results with:initial
  5. [5]
    Erlang/OTP 18.0
    Major release : otp_src_18 Build date : 2015-06-24 18 is a major new release of Erlang/OTP. You can download the full source distribution from http://www.erlang ...
  6. [6]
    [erlang-questions] Change of license from EPL to APL 2.0
    Mar 23, 2015 · ... license of Erlang from the Erlang Public License (EPL) to the Apache 2.0 License (APL). For a smooth transition we need cooperation from all ...
  7. [7]
    Versions — Erlang System Documentation v28.1.1
    The application versions identified by an OTP version corresponds to application versions that have been tested together by the Erlang/OTP team at Ericsson AB.
  8. [8]
    Build a Mnesia Database - Erlang
    Building a Mnesia database involves defining a schema, starting Mnesia, and creating tables. The schema is a special table with table names and storage types.
  9. [9]
    Transactions and Other Access Contexts — mnesia v4.24.1
    A Mnesia transaction is a mechanism by which a series of database operations can be executed as one functional block.
  10. [10]
    Mnesia System Information - Erlang/OTP
    To dump the tables to disc, all replicas must have the storage type ram_copies . The table content is placed in a .DCD file on the disc. When the Mnesia system ...Missing: backends | Show results with:backends
  11. [11]
  12. [12]
  13. [13]
  14. [14]
  15. [15]
  16. [16]
  17. [17]
  18. [18]
  19. [19]
  20. [20]
  21. [21]
    Getting Started — mnesia v4.24.1 - Erlang
    To start Mnesia, specify the directory, initialize a schema, start Mnesia, and create a table. Example: `erl -mnesia dir '"/tmp/funky"'` and `mnesia:start()`.
  22. [22]
  23. [23]
  24. [24]
  25. [25]
  26. [26]
    Introduction — mnesia v4.24.1 - Erlang
    This User's Guide describes how to build Mnesia-backed applications, and how to integrate and use the Mnesia database management system with OTP.
  27. [27]
  28. [28]
  29. [29]
  30. [30]
  31. [31]
  32. [32]
  33. [33]
  34. [34]
  35. [35]
  36. [36]
  37. [37]
  38. [38]
    Mnesia database | ejabberd
    Jan 3, 2005 · Some data is stored in Mnesia (permanent database, available in RAM while being used and stored in files every some time), other is on ETS ( ...
  39. [39]
    Mnesia database stats and performance | ejabberd
    Oct 1, 2015 · Mnesia should be used to store only small amount of data. If you have a large deployment or plan to store a lot, you should store in a ...
  40. [40]
    Metadata store | RabbitMQ
    With Mnesia, it's up to RabbitMQ to resolve conflicts in the data if two nodes could not communicate for a while and the database was updated on one side (e.g. ...
  41. [41]
    From Mnesia to Khepri, Part 1: Khepri Overview and Motivation
    Apr 2, 2024 · Since its inception, RabbitMQ has used the Mnesia DB for metadata persistence. However, this will soon change– Khepri will replace Mnesia in ...
  42. [42]
    [PDF] Mnesia - Erlang/OTP
    This User's Guide describes how to build Mnesia-backed applications, and how to integrate and use the Mnesia database management system with OTP.
  43. [43]
    How WhatsApp Grew to Nearly 500 Million Users, 11,000 cores, and ...
    Mar 31, 2014 · Mnesia: Because they don't use transactions to get as much consistency as possible they serialize access to records on a single process on a ...
  44. [44]
    Understanding WhatsApp's Architecture & System Design
    Oct 11, 2021 · To store data and temporary messages, WhatsApp uses an Erlang-based, distributed DBMS (Database Management System) called Mnesia. This DBMS ...
  45. [45]
    The fellowship of the forgotten. How we migrated from Mnesia to ...
    Feb 26, 2025 · In part2 we will focus on database isolation levels and how we managed to make Postgres behave like Mnesia and how we ensured our implementation ...
  46. [46]
    Guardians of consistency. The Quest for Mnesia-Like Compatibility
    Mar 12, 2025 · A key thing is that Mnesia provides transactional properties such as atomicity, consistency, isolation and durability just as any other SQL ...
  47. [47]
    How EMQX under the new architecture of Mria + RLOG achieves ...
    Aug 3, 2022 · Embedded: Unlike databases such as MySQL and PostgreSQL, Mnesia and EMQX run in the same operating system process (similar to SQLite).
  48. [48]
  49. [49]
  50. [50]
  51. [51]
    11 Mnesia Database Questions - Erlang/OTP
    For ram_copies and disc_copies, the entire table is kept in memory, so data size is limited by available RAM. · disc_only_copies tables are limited to 2 GB each.
  52. [52]
    Mnesia: advantages and differences
    Jan 4, 2011 · ejabberd consumes less computational resources when using some *SQL database than when using internal Mnesia. You are probably interested in ...
  53. [53]
  54. [54]
  55. [55]