Database / Mnesia intermediate to advanced Interview questions
1. What is the difference between mnesia:transaction/1 and mnesia:sync_transaction/1?
Both give the same atomicity and locking guarantees, but they differ in when they return control to the caller relative to replication. mnesia:transaction/1 can return as soon as the commit is decided locally, while replication to other nodes finishes asynchronously in the background. mnesia:sync...
2. What is the difference between async_dirty and sync_dirty activity contexts?
Both are non-transactional access contexts you can use with mnesia:activity/2 or by directly calling dirty functions, trading Mnesia's usual locking/atomicity for speed — the difference between them is, again, about replication timing rather than locking. async_dirty sync_dirty Returns as s...
3. How does mnesia:activity/4 let you customize the access context of an operation?
mnesia:activity(AccessContext, Fun, Args, Module) is the general-purpose entry point underlying transaction/1 , sync_transaction/1 , and the dirty variants — those are really just convenience wrappers around activity/4 with a specific context pre-selected. mnesia:activity(transaction, fun u...
4. What is table fragmentation in Mnesia and why would you use it?
Fragmentation splits a single logical table into multiple physical fragments, each of which can live on a different node (or set of nodes), while application code still addresses it as one table name. It exists for tables whose size or write volume would overwhelm what a single, fully-replicated ...
5. How do you configure a fragmented Mnesia table?
Fragmentation is set up via the frag_properties option on mnesia:create_table/2 , specifying how many fragments to create and how to distribute them across nodes. m nes ia : crea te _ ta ble(sessio ns , [{ fra g_proper t ies , [{ n ode_pool , [ n ode 1 @hos t , n ode 2 @hos t , n ode 3 @hos t ]},...
6. Why does fragmentation help Mnesia scale beyond what one table replica set can handle?
A normal, fully-replicated Mnesia table means every node holding a copy stores (and, for disc_copies , durably persists) the entire dataset, and every write must be coordinated and applied across all of those full copies via two-phase commit. Both the storage requirement and the write coordinatio...
7. What is the difference between mnesia:match_object/1 and mnesia:select/2?
Both let you query beyond a simple primary-key lookup, but match_object/1 uses a simpler, pattern-only interface, while select/2 supports full match specifications with guard conditions. mnesia:match_object/1 mnesia:select/2 A single pattern with `'_'` wildcards; returns whole matching records. P...
8. What is mnesia:all_keys/1 used for?
mnesia:all_keys(Table) returns every primary key currently stored in a table, run inside a transaction (or a dirty context via mnesia:dirty_all_keys/1 ) — a quick way to enumerate a table's contents by key without writing a match specification. mnesia:transaction(fun() -> mnesia:all_keys(pe...
9. How do you integrate Mnesia with QLC for complex queries?
QLC (Query List Comprehension) provides a SQL-like, declarative query syntax over any "queryable" data source, and Mnesia tables can be exposed to it via mnesia:table/1 , letting you write joins, sorting, and filtering across one or more tables in a single expression. mnesia:transaction(fun() -> ...
10. Why would you use qlc:q with mnesia:table/1 instead of mnesia:select/2?
select/2 is a single-table, match-specification-based query — excellent for filtering one table efficiently, but it can't express a relationship spanning multiple tables in one query. QLC's comprehension syntax reads much closer to how you'd describe the query in plain language (join these ...
11. What is a sticky lock in Mnesia and why does it improve performance?
Acquiring a write lock on a replicated table normally requires coordinating with every node holding a copy, even if, in practice, all the writes to a particular record consistently originate from the same node. A sticky lock lets a node "claim" ownership of a table's locking for itself after its ...
12. When can sticky locks cause a problem in a distributed Mnesia cluster?
A sticky lock optimizes for the common case where the same node keeps writing to a table, but it means lock "ownership" has to be reclaimed if a different node suddenly needs to write — and if the node currently holding the sticky lock has become unreachable (crashed, network partition), re...
13. How do you dynamically add a new node to a running Mnesia cluster?
Bringing a new node into an already-running Mnesia cluster involves connecting it at the Erlang distribution level first, then explicitly telling Mnesia's schema about it via mnesia:change_config(extra_db_nodes, [NewNode]) , which merges the new node into the existing schema rather than creating ...
14. What is mnesia:change_config(extra_db_nodes, Nodes) used for?
This call tells the local Mnesia instance about additional nodes it should try to connect and merge schema information with — it's the mechanism behind both joining a brand-new node to an existing cluster and reconnecting a node that was started independently but should now become part of a...
15. How do you remove a table replica from a node without dropping the whole table?
mnesia:del_table_copy(Table, Node) removes just one node's copy of a table from the schema and frees its local storage, while the table itself keeps running normally on every other node still holding a copy — it's the inverse of add_table_copy/3 . mnesia:del_table_copy(session, 'nodeb@host'...
16. What is mnesia:del_table_copy/2 used for?
mnesia:del_table_copy(Table, Node) updates the schema to stop treating the given node as a replica of the specified table, and reclaims whatever storage (memory and/or disk) that copy was using on that node — a schema-level operation, run as part of deliberate cluster maintenance rather tha...
17. How do you move a table copy from one node to another while the system stays live?
mnesia:move_table_copy(Table, FromNode, ToNode) relocates a table's replica from one node to another without taking the table offline — useful for rebalancing load, retiring a node, or migrating data ahead of decommissioning old hardware. mnesia:move_table_copy(session, 'old_node@host', 'ne...
18. What is mnesia:move_table_copy/3 used for?
This function is the standard tool for live data rebalancing and node decommissioning: moving a specific table's replica to a different node without a maintenance window where the table is unavailable. mnesia:move_table_copy(person, 'nodeA@old_host', 'nodeD@new_host'). Common scenarios: migrating...
19. What is the {majority, true} table option and what tradeoff does it introduce?
Setting {majority, true} on a table requires that a write be acknowledged by a majority of the nodes holding a copy of that table before the transaction is allowed to commit — if a network partition leaves a node (or minority group of nodes) unable to reach a majority of replicas, writes on...
20. Why would you enable the majority option on a table prone to network partitions?
Without the majority option, a partition can let two isolated groups of nodes both keep accepting writes to the same replicated table independently, setting up an inconsistent-database conflict that has to be manually or programmatically resolved once the partition heals — and any conflicti...
21. What is a local_content table in Mnesia?
A local_content table is replicated in the sense that its schema exists on every listed node, but each node keeps its own independent data in it — writes on one node are never propagated to other nodes' copies, unlike a normal replicated table where every copy is meant to converge on the sa...
22. When would you use a local_content table instead of a normal replicated table?
Reach for local_content specifically when the data is conceptually per-node rather than shared — each node's copy is meant to hold genuinely different information, not a synchronized replica of the same logical dataset. A normal replicated table is the wrong fit here since it's designed aro...
23. What is mnesia:set_master_nodes/2 used for?
mnesia:set_master_nodes(Table, Nodes) designates specific nodes as the authoritative source for a table's data when Mnesia needs to decide which replica to trust — most notably when recovering from an inconsistent database state after a network partition, where by default Mnesia can't autom...
24. How does setting master nodes influence conflict resolution after a network partition?
When a partition heals and Mnesia detects that a table's replicas diverged, its default behavior is to raise an inconsistent_database_event and leave the decision of which side to trust to an operator or a configured event handler, since it has no inherent basis to prefer one side over the other....
25. What is mnesia_tm and what role does it play internally?
mnesia_tm is Mnesia's internal transaction manager process — the component actually responsible for coordinating a transaction's lifecycle: acquiring locks, tracking what a transaction has read/written, driving the two-phase commit exchange with other nodes for replicated tables, and decidi...
26. How does Mnesia's transaction manager serialize concurrent transactions touching the same records?
Mnesia uses pessimistic locking rather than optimistic concurrency control: when a transaction reads or writes a record, mnesia_tm acquires the appropriate lock (read or write) on that record before proceeding, and a conflicting lock request from another concurrent transaction simply has to wait ...
27. Why can a "hot" record or table become a bottleneck under heavy Mnesia write load?
Because Mnesia uses per-record (or sometimes per-table) locking inside transactions, any record that many concurrent transactions all need to write to becomes a serialization point: every transaction touching it has to wait its turn for the write lock, no matter how many CPU cores or schedulers a...
28. How can you redesign a schema to reduce lock contention on a frequently-updated record?
The core fix is usually to split one hot, shared record into several independent pieces that different transactions can update without contending for the same lock, then combine them only when actually reading a final value. %% instead of one shared counter record: %% #counter{name = hits, value ...
29. What is mnesia:subscribe/1 used for?
mnesia:subscribe/1 registers the calling process to receive events about Mnesia activity — table changes, system events like node up/down, or schema changes — delivered as ordinary messages, letting application code react to data changes without polling. mnesia:subscribe({table, perso...
30. How do you react to table events using Mnesia's subscription mechanism?
After subscribing, the subscribing process simply receives ordinary messages in its mailbox whenever a matching event occurs, and handles them with a normal receive clause — there's no separate callback registration mechanism the way gen_event handlers work; it's just messages. mnesia:subsc...
31. What is a nested Mnesia transaction and how does it behave differently from a top-level one?
Calling mnesia:transaction/1 from inside code that's already running within another transaction creates a nested transaction . Rather than being a fully independent transaction, it shares the outer transaction's locks and only really commits when the outermost transaction itself commits — a...
32. Why should nested transactions generally be avoided or used carefully in Mnesia?
Since a nested transaction shares the outer transaction's fate (an inner abort takes down the whole outer transaction), relying on nesting to isolate a "risky" operation from the rest doesn't actually provide the isolation you might expect from true nested/savepoint semantics in some relational d...
33. What is the dc_dump_limit configuration parameter used for?
Mnesia doesn't write every single transaction directly and immediately into a disc_copies table's on-disk data file — it appends to a transaction log first, periodically "dumping" that log into the actual table files. dc_dump_limit controls how large that log is allowed to grow (as a multip...
34. Why does tuning the transaction log dump frequency matter for write-heavy Mnesia workloads?
Every write to a disc_copies table involves appending to a log file, which is cheap, versus periodically folding that log into the actual on-disk table representation, which is comparatively expensive (it involves more disk I/O restructuring the actual data file). Dumping too frequently adds over...
35. How do you merge two previously-separate Mnesia clusters into one?
Mnesia doesn't provide a single built-in "merge two schemas" command — merging two independently created databases is a deliberate, mostly manual migration process, since each side already has its own complete schema and potentially overlapping table names or keys. %% t ypical approach : ex...
36. What complications arise when merging schemas from two independently-created Mnesia databases?
The core problem is that each database's schema and table content evolved independently, with no shared history — so there's no inherent way for Mnesia to know how to reconcile them. Concretely: Key collisions — the same primary key value may exist in both databases referring to compl...
37. How does Mnesia decide which node's data wins during schema merge conflicts?
Left to its own defaults, Mnesia doesn't have an inherent basis for deciding whose data "wins" — when two nodes reconnect and discover their schemas or table contents disagree, it raises an inconsistent_database_event rather than guessing. Resolution happens through one of a few explicit me...
38. What is the difference between a global lock and a per-record lock in Mnesia transactions?
A per-record lock (the default behavior of read/1 / write/1 inside a transaction) only blocks other transactions from touching that specific record, letting unrelated concurrent transactions on other records in the same table proceed freely. A global (table-level) lock, taken via mnesia:read_lock...
39. When would you use mnesia:read_lock_table/1 instead of relying on per-record locks?
Table-level locking makes sense specifically when an operation needs a consistent view of, or exclusive access to, the entire table at once — something per-record locks can't provide, since they only protect individual rows from concurrent modification while leaving the rest of the table op...
40. How do you profile and optimize Mnesia transaction throughput in a write-heavy system?
Profiling starts with identifying where time is actually going: lock contention on specific records/tables, replication coordination overhead across nodes, or transaction log dump frequency — each has a different fix. Check for hot records/tables via tracing or by inspecting which transacti...
41. Why is batching multiple related writes into a single transaction usually better than many small transactions?
Every transaction carries fixed overhead beyond the actual work it does: acquiring locks, and for replicated tables, coordinating a two-phase commit round-trip with every replica. Running ten related writes as ten separate transactions pays that coordination overhead ten times over; wrapping them...
42. How does Mnesia's schema interact with OTP release upgrades (appup/relup)?
A release upgrade's appup instructions describe how to hot-swap module code and transform running gen_server state via code_change/3 , but Mnesia's schema and table data live outside that per-process state entirely — changing a Mnesia record's shape isn't something code_change/3 touches, si...
43. What is the difference between mnesia:dirty_all_keys/1 and mnesia:all_keys/1?
Both return every primary key in a table, but they differ in the same way other dirty/transactional pairs do: mnesia:all_keys/1 must run inside a transaction (or another activity context) and takes the appropriate lock, giving you a result consistent with the rest of that transaction's view of th...
44. Explain the internal working of how Mnesia chooses which replica to read from in a load-balanced cluster?
For a table replicated across multiple nodes, Mnesia tracks a per-table where_to_read preference — by default, favoring reading from the local node's own copy if it holds one, since a local read avoids any network round-trip entirely. flowchart TD A[Read request for Table] --> B{Does this n...
45. What is the where_to_read table_info item used for?
mnesia:table_info(Table, where_to_read) returns which node the local Mnesia instance would currently read that table from — useful for diagnosing read-routing behavior directly, rather than inferring it indirectly from latency measurements. mnesia:table_info(session, where_to_read). %% -> n...
46. What is mnesia:dump_log/0 used for?
mnesia:dump_log/0 forces an immediate dump of the current transaction log into the actual on-disk table files, rather than waiting for the automatic threshold (governed by dc_dump_limit ) to be reached naturally. mnesia:dump_log(). %% -> dumped It's mainly useful around planned maintenance moment...
47. How do you handle a transaction that needs to retry after being aborted due to a deadlock?
When Mnesia detects a deadlock between two transactions, it aborts one of them automatically, but it doesn't retry it for you — the calling code gets back {aborted, Reason} and is responsible for deciding whether and how to retry. retry_transaction(Fun, Retries) when Retries > 0 -> case mne...
48. What is the difference between mnesia:transaction/1's built-in behavior and manual retry logic you might add?
mnesia:transaction/1 itself already retries internally in one specific, narrow case: if the transaction is aborted purely because of a lock conflict that Mnesia's own internal deadlock resolution detected as safely retryable, it can re-run the transaction function automatically without the caller...