Amnesia problems and workarounds

Hey, today I give amnesia library a try and found a few problems. I would like describe how to setup it properly and solve problems which could occur due to using it in my specific situation.

Problem with Elixir 1.7

It’s not easy even at start. When I have added {:amnesia, "~> 0.2.7"} to my dependencies then exquisite (amnesia dependecy) gives me such error:

==> exquisite
Compiling 1 file (.ex)

== Compilation error in file lib/exquisite.ex ==
** (CompileError) lib/exquisite.ex:103: __CALLER__ is available only inside defmacro and defmacrop
    lib/exquisite.ex:103: (module)
could not compile dependency :exquisite, "mix compile" failed. You can recompile this dependency with "mix deps.compile exquisite", update it with "mix deps.update exquisite" or clean it with "mix deps.clean exquisite"

Fortunately it’s already solved in master branch (just no new hex package), so solution is as easy as adding: {:exquisite, "~> 0.1.8", github: "meh/exquisite", override: true} to project dependencies. This will force mix to use github version (defaults to master branch) by ignoring amnesia dependency declaration.

Weird database compilation problem

The next problem occurs also at compilation. However both exquisite and amnesia compiles correctly. The error looks like:

Compiling … files (.ex)

== Compilation error in file lib/my_app/database.ex ==
** (UndefinedFunctionError) function Kernel.Typespec.defines_type?/3 is undefined or private. Did you mean one of:

      * defines_type?/2

    (elixir) Kernel.Typespec.defines_type?(Database.TableName, :t, 0)
    lib/my_app/database.ex:…: (module)
    lib/my_app/database.ex:…: (module)

This error occurs, because amnesia is using Elixir private API. Such code could change really often and developers should not depend on it. Fortunately fixing it is pretty easy. Simply navigate to: deps/amnesia/lib/amnesia/table/definition.ex and replace line 938:

        unless Kernel.Typespec.defines_type?(__MODULE__, :t, 0) do

with this one:

        unless Kernel.Typespec.defines_type?(__MODULE__, {:t, 0}) do

Note: Don’t forget to run mix deps.compile to force recompile our change. Without it command error would occur again, because this dependency is already compiled and such old compiled version is used instead. Of course it’s not a good production solution, but temporary tip which allow use it now without waiting on package owner.

Multiple nodes in test environment

Last problem which I found is how to properly use amnesia in test environment. We are able to create simple cluster without any extra commands, but amnesia would fail lots of times. For example:

Calling Amnesia.Schema.create(nodes) would give us:

{:error,
 {'Cannot install fallback',
  {'No disc resident schema on local node',
   …}}}

Calling Database.create!(disk: nodes) would even freeze our prompt without any warning and/or error.

This could be solved by using amnesia on any slave nodes instead master node.

Helpful resources

  1. Compilation error after upgrading elixir #10 issue at meh/ exquisite
    This is related to first problem I described.

  2. PSA: Do not use private APIs, request a feature instead
    This is related to second problem I described. @josevalim describes the problem with lots of dependencies and projects when trying to update to Elixir version 1.7

  3. Starting multiple named nodes locally for ExUnit tests
    Here @beardedeagle summaries answers in his topic and is giving of cluster example which could cause some troubles with amnesia for newbies.

  4. Finally here is Elixir + Phoenix + Amnesia + Multi-node article written by @jmerriweather which inspires me to give amnesia a try and write this topic.

Hope that I have explained everything correctly. Let me know if you have any questions. I would like to say thank you for everyone which helps community. Without all of you such problems would definitely took me much more time than I spend today in this topic.

7 Likes

I can’t edit this post, so I will just link one question I have posted:

This topic has described solution to automatically adding new nodes (standard way described in linked tutorial assumes that all nodes are already prepared).

2 Likes

I gave Amnesia a try few months ago, with a distributed setup involving at least 2 nodes on production at any given time. Since then i rewrote those parts of app to simply use mnesia directly. IMHO Amnesia is too little convenience and way too much hassle.

Also i couldn’t get rid of some dialyzer errors when using Mnesia, I have a feeling either I didn’t understand what some functions/macros do, or there are some typespec problems in Mnesia itself.

Just my 2 cents.

1 Like

For now I do not have any problems with mnesia itself, but amnesia have few really weird warnings which just should not appear. I have created an ignore file with such content:

[
  {"lib/my_app/database.ex"},
  {":0:unknown_type Unknown type: Amnesia.o/0."},
  {":0:unknown_type Unknown type: Amnesia.Table.Selection.t/0."},
  {":0:unknown_type Unknown type: Amnesia.Table.Stream.t/0."},
  {":0:unknown_type Unknown type: Selection.t/0."}
]

I will simply keep this configuration until amnesia developers would solve dialyzer warnings. With such file I do not have any errors.

Note: Of course I have no extra @spec declarations in lib/my_app/database.ex.

Sorry, I meant Amnesia, not Mnesia. I don’t know how I messed it up :joy: I have no problems with Mnesia, The Erlang one :wink: It simply works, is easy to set up, easy to use, don’t give me strange dialyzer errors. But i have problems with Amnesia, the wrapper wrote in Elixir :wink: I don’t know exactly what is it, but Mnesia seems simpler to use and more straightforward than Amnesia, when it comes to my use cases :slight_smile: