cult3

Working with Mnesia in Elixir

Oct 12, 2016

Table of contents:

  1. What is Mnesia?
  2. Starting Mnesia
  3. Creating tables
  4. Inserting data
  5. Reading data
  6. Conclusion

Last week we looked at using ETS as a storage mechanism for storing any Erlang term in an Elixir application.

ETS is part of OTP and so as an Elixir developer you can automatically start using it without setting anything up. This is pretty crazy when you think about it, what other languages ship with something like Redis out of the box?

Well the story gets a little bit crazier…

Erlang actually ships with a real-time distributed database management system called Mnesia! In today’s tutorial we are going to be looking at working with Mnesia in Elixir.

What is Mnesia?

Mnesia is a “distributed, soft real-time database management system” that, like ETS, ships as part of OTP. This means as an Elixir developer, you can use Mnesia by default without having to install any third-party dependencies.

Mnesia is actually built on top of ETS and DETS to provide additional functionality that you might expect from a storage mechanism. For example, Mnesia gives you the persistence of DETS, with the performance of ETS. This allows you to store your information on RAM or disk, but it does also mean you have the size constraints of DETS.

Transactions are another important feature of Mnesia that you might expect from a storage mechanism. Mnesia allows you to perform multiple operations on one or more tables as a single transaction.

Mnesia also allows you to store your data across multiple nodes, which gives you the benefit of distributed storage, but also has the drawbacks of distributed storage. Mnesia sits on the CP side of the of the CAP theorem.

But probably the biggest benefit of using Mnesia as a storage mechanism is that you can store Erlang terms directly without having to translate to a different format for storage.

Starting Mnesia

As I mentioned earlier, Mnesia ships as part of Erlang and OTP and so you already have access to everything you need when you install Elixir.

Mnesia is available through the interoperability of Elixir and Erlang, and so in order to access Mnesia you need to use the colon syntax.

However, before we can actually start using Mnesia to store data, first we need to pass it the nodes in which it should run on:

:mnesia.create_schema([node])

In the example above, I’m using the node/0 function to pass the current node as a list to the create_schema/1 function.

We haven’t talked about distributed nodes yet in this exploration of Elixir, but theoretically if we were using multiple nodes we could pass them as a node list. This is how Mnesia is distributed, because it runs on multiple nodes that you can define like this.

Next we can call start/0 to actually start Mnesia:

:mnesia.start()

If we were running Mnesia on multiple nodes we would have to run the start function on each node.

Creating tables

To create a new table you use the create_table/2 function and pass the name of the table as the first argument and a keyword list of columns as the second argument:

:mnesia.create_table(User, attributes: [:id, :first_name, :last_name, :username, :email])

Inserting data

To insert data you pass an anonymous function to the transaction/1 function. Inside the transaction you use the write/1 function and pass it a tuple of data to insert:

:mnesia.transaction(fn ->
  :mnesia.write({User, 1, "Philip", "Brown", "philipbrown", "phil@ipbrown.com"})
end)

You can insert many rows within the same transaction. If the transaction is successful you will be returned a tuple where the first element is the atom :atomic and the second element is the atom :ok.

Reading data

To read data, you pass an anonymous function to the transaction/1 function again, but this time you use the read/1 function to select the row you want:

{:atomic, [record]} =
  :mnesia.transaction(fn ->
    :mnesia.read({User, 1})
  end)

If the read was successful you will be returned a tuple where the first element is the atom :atomic and the second element is a list of results.

Conclusion

Mnesia is quite an interesting aspect of using Erlang and Elixir for the simple fact that it’s a distributed, soft real-time database management system that ships as part of the language.

That is quite a unique thing when it comes to what you get out of the box with your programming language of choice.

Mnesia is really quite powerful and so we have only really just scratched the surface of what is possible in today’s tutorial. I’m still trying to get my head around Mnesia myself.

From what I’ve read, it seems like Mnesia has a sweet spot of storing a limited amount of data on a fixed number of nodes. This is probably ideal if you want to persist data, but you don’t want to the headache of dealing with a database.

And of course, Mnesia allows you to persist data right inside of Erlang (or Elixir)!

As we continue with this exploration of Elixir, I’m sure we will no doubt stumble upon a number of opportunities where we can use Mnesia for storage where traditionally we would of required an external database dependency.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.