I’m observing behavior with mnesia that is unexpected by me and could use some help understanding what’s going on. I’ve tested this with erlang 21.0.1 and 21.2.2, and elixir 1.7.4.
Given two scripts:
write.exs
IO.puts "Creating schema..."
:ok = :mnesia.create_schema([node()])
IO.puts "Starting mnesia..."
:ok = :mnesia.start()
IO.puts "Creating table..."
{:atomic, :ok} = :mnesia.create_table(:example, [{:disc_only_copies, [node()]}, type: :set, attributes: [:name, :value]])
IO.puts "Setting :foo = 1..."
{:atomic, :ok} = :mnesia.transaction(fn -> :mnesia.write({:example, :foo, 1}) end)
IO.puts "Looking up :foo..."
:mnesia.transaction(fn -> :mnesia.read({:example, :foo}) end) |> IO.inspect
IO.puts "Done."
read.exs
IO.puts "Starting mnesia..."
:ok = :mnesia.start()
IO.puts "Waiting until table is loaded..."
:mnesia.wait_for_tables([:example], 1_000)
IO.puts "Looking up :foo..."
:mnesia.transaction(fn -> :mnesia.read({:example, :foo}) end) |> IO.inspect
IO.puts "Done."
When I run these scripts:
rm -rf Mnesia.nonode@nohost; mix run write.exs; mix run read.exs
I observe the following:
Creating schema...
Starting mnesia...
Creating table...
Setting :foo = 1...
Looking up :foo...
{:atomic, [{:example, :foo, 1}]}
Done.
Starting mnesia...
Waiting until table is loaded...
dets: file "Mnesia.nonode@nohost/example.DAT" not properly closed, repairing ...
Looking up :foo...
{:atomic, []}
Done.
The primary behavior that is unexpected to me is that writing to a disc only table inside a transaction doesn’t seem to ensure that the record is actually written to disc before moving on after the transaction. ie, it’s not actually durable. I do observe the dets warning that the table file was not properly closed, which I presume is because the script terminated abruptly (though normally).
If I explicitly call :mnesia.stop
before the end of the write.exs
script, the record is actually persisted and successfully read by the read.exs
script, however I expect that I should be able to trust that once the transaction returns, the record is safely persisted. I can include :mnesia.stop
in my shutdown procedures, but it does not appear that it’s actually durable in the case where the application crashes or is halted via kill -9
.
If instead of stopping :mnesia I sleep for a few seconds at the end of write.exs
, the dets warning is still emitted but the record is successfully found. So it does eventually write the record to disc without an explicit :mnesia.stop
, but just not before the :mnesia.transaction call returns, which very clearly to me seems to conflict with what is said here: http://erlang.org/doc/apps/mnesia/Mnesia_chap4.html#durability
I’ve observed the same behavior with :disc_copies
and using :mnesia.sync_transactionas well, just to be thorough. The following is the output from
:mnesia.system_info(:all)`, in case it’s useful. It’s all default values afaik. (paths have been truncated by me)
[
access_module: :mnesia,
auto_repair: true,
backend_types: [:ram_copies, :disc_copies, :disc_only_copies],
backup_module: :mnesia_backup,
checkpoints: [],
db_nodes: [:nonode@nohost],
debug: :none,
directory: 'Mnesia.nonode@nohost',
dump_log_load_regulation: false,
dump_log_time_threshold: 180000,
dump_log_update_in_place: true,
dump_log_write_threshold: 1000,
event_module: :mnesia_event,
extra_db_nodes: [],
fallback_activated: false,
held_locks: [],
ignore_fallback_at_startup: false,
fallback_error_function: {:mnesia, :lkill},
is_running: :yes,
local_tables: [:schema, :example],
lock_queue: [],
log_version: '4.3',
master_node_tables: [],
max_wait_for_decision: :infinity,
protocol_version: {8, 3},
running_db_nodes: [:nonode@nohost],
schema_location: :opt_disc,
schema_version: {2, 0},
subscribers: [#PID<0.141.0>],
tables: [:schema, :example],
transaction_commits: 2,
transaction_failures: 0,
transaction_log_writes: 0,
transaction_restarts: 0,
transactions: [],
use_dir: true,
core_dir: false,
no_table_loaders: 2,
dc_dump_limit: 4,
send_compressed: 0,
version: '4.15.5'
]