ecto_mnesia
ecto_mnesia copied to clipboard
Mnesia dir changes based upon context in umbrella project
Using Ecto 2.1.4, ecto_mnesia 0.9.0, and Elixir 19, there appears to be inconsistent behavior with respect to the path to mnesia's disc copies in umbrella projects.
During the execution of ecto.create and ecto.migrate, the mnesia files for the app under the umbrella are created relative to the app, as one might expect:
# run from the root of the umbrella, with config :mnesia, dir: 'priv/thingy/data' in config.exs
$ mix ecto.create
18:40:42.973 [info] ==> Setting Mnesia schema table copy type
18:40:42.984 [info] ==> Ensuring Mnesia schema exists
==> under_umbrella
The database for Thingy has been created
$ mix ecto.migrate
18:40:46.983 [debug] Selecting all records by match specification `[{{:schema_migrations, :"$1", :"$2"}, [], [[:"$1"]]}]` with limit nil
18:40:47.005 [info] == Running Thingy.Migrations.MakeThingies.change/0 forward
18:40:47.005 [info] create table if not exists thingies
18:40:47.024 [info] == Migrated in 0.0s
$ ls apps/under_umbrella/priv/thingy/data/
DECISION_TAB.LOG LATEST.LOG id_seq.DCD schema.DAT schema_migrations.DCD schema_migrations.DCL schema_migrations.TMP thingies.DCD
However, when the umbrella project is run, mnesia appears to be looking at the configured path relative to the root of the umbrella project, not relative to the app:
# run from the root of the umbrella, with config :mnesia, dir: 'priv/thingy/data' in config.exs
$ iex -S mix
Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.4.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :mnesia.info
---> Processes holding locks <---
---> Processes waiting for locks <---
---> Participant transactions <---
---> Coordinator transactions <---
---> Uncertain transactions <---
---> Active tables <---
schema : with 1 records occupying 421 words of mem
===> System info in version "4.14.3", debug level = none <===
opt_disc. Directory "/mnesia_paths/priv/thingy/data" is NOT used.
use fallback at restart = false
running db nodes = [nonode@nohost]
stopped db nodes = []
master node tables = []
remote = []
ram_copies = [schema]
disc_copies = []
disc_only_copies = []
[{nonode@nohost,ram_copies}] = [schema]
2 transactions committed, 0 aborted, 0 restarted, 0 logged to disc
0 held locks, 0 in queue; 0 local transactions, 0 remote
0 transactions waits for other nodes: []
:ok
You'll notice that it's looking for the mnesia files in /priv/thingy/data, not /apps/under_umbrella/priv/thingy/data, and so things don't work. If you run the ecto.create and ecto.migrate commands with the aforementioned configuration, and then change config.exs to use apps/under_umbrella/priv/thingy/data before running iex, things work as expected:
# run from the root of the umbrella, with config :mnesia, dir: 'apps/under_umbrella/priv/thingy/data' in config.exs
$ iex -S mix
Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.4.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :mnesia.info
---> Processes holding locks <---
---> Processes waiting for locks <---
---> Participant transactions <---
---> Coordinator transactions <---
---> Uncertain transactions <---
---> Active tables <---
schema_migrations: with 1 records occupying 115 words of mem
schema : with 4 records occupying 774 words of mem
thingies : with 0 records occupying 304 words of mem
id_seq : with 0 records occupying 304 words of mem
===> System info in version "4.14.3", debug level = none <===
opt_disc. Directory "/mnesia_paths/apps/under_umbrella/priv/thingy/data" is used.
use fallback at restart = false
running db nodes = [nonode@nohost]
stopped db nodes = []
master node tables = []
remote = []
ram_copies = []
disc_copies = [id_seq,schema,schema_migrations,thingies]
disc_only_copies = []
[{nonode@nohost,disc_copies}] = [id_seq,thingies,schema,schema_migrations]
2 transactions committed, 0 aborted, 0 restarted, 0 logged to disc
0 held locks, 0 in queue; 0 local transactions, 0 remote
0 transactions waits for other nodes: []
:ok
However, I don't know if there is a way to change the configuration based on context, though I doubt this is intentional behavior. I've created a repo that demonstrates the issue for you here.
Please let me know if you need anything else.
Hello,
Thanks for a detailed feedback. Unfortunately adapter itself has nothing to do with Mnesia migrations (there we simply use Erlang implementation), however I'll try to look closer if we can fix it on our side.
Just in case anyone else is having a similar issue and needs a workaround, use the following config for mnesia in your app's config.exs:
config :mnesia, dir: to_charlist Path.join(File.cwd!, "priv/data")
It seems that mix tasks run relative to the path of the mix file. Consequently, this change will make it so running mix ecto.create and mix ecto.migrate from the root of the umbrella will create the mnesia files in the /priv/data directory under the umbrella root, which is the same location that will be found by mnesia when running iex -S mix from the umbrella root.
This workaround is far from ideal, since the behavior is different depending on where you run mix and iex. If you run mix ecto.create and mix ecto.migrate from the root of the umbrella, the files will be created in the place mentioned above. If you then run iex from inside the app itself (like if debugging something in the app), the mnesia database wouldn't be available, since it'd be looking for its data files in /apps/my_app/priv/data. The same holds true if you run mix ecto.create and mix ecto.migrate from the root of the app and then run iex from the umbrella - it'd be looking for its data files in /priv/data after they were created in /apps/my_app/priv/data.
If you always (or usually) interact with your project from the root of the umbrella, this approach will get you over the hump until a more permanent solution is found, though.
Hi, I'm using this workaround and it seems to do the same thing whether I'm in the umbrella root or the apps/<project> dir
config :mnesia, :dir, [__DIR__, "..", "priv", "data"]
|> Path.join()
|> Path.expand()
|> to_charlist()