Is there a built-in mechanism for us to launch Mnesia in Elixir? - mnesia

It seems like that we can launch Mnesia only by typing iex --erl "--mnesia dir '~/doc/'" --name mynode.
Can we just launch it without passing arguments to erl?

You can set it up dynamically in your code. All the --erl instruction above does is to configure the mnesia application before it is started. You could achieve this by:
# First load mnesia
iex(1)> :application.load(:mnesia)
:ok
# Now configure the desired directory
iex(2)> :application.set_env(:mnesia, :dir, 'sample')
:ok
# Start mnesia as usual
iex(3)> :mnesia.start
:ok
# See if the desired info is correct
iex(4)> :mnesia.info
:ok
Notice it only works if you start the application manually. If you are starting it automatically and want to configure through the command line, your current snippet is the best option (or using ELIXIR_ERL_OPTS).

Related

How to rollback database in docker container using elixir phoenix releases and the example MyApp.Release.rollback in the guides

I cannot figure out how to rollback a database when trying to do it through a phoenix app running in a docker container. I am trying to simulate locally what it would be like when migrating on a remote server.
I am running it locally by running:
docker run -it -p 4000:4000 -e DATABASE_URL=ecto://postgres:postgres#host.docker.internal/my_app_dev -e SECRET_KEY_BASE=blahblah my-app-tag:v1
I view the running containers with:
docker ps
I bash into the container
docker exec -it 8943918c8f4f /bin/bash
cd into app/bin
cd bin
try to rollback
./my_app rpc 'MyApp.Release.rollback(MyApp.Repo, "20191106071140")'
=> 08:43:45.516 [info] Already down
If this did indeed work when running through the application it should blow up as I do different things. But it doesn't.
If I try eval
./my_app eval 'MyApp.Release.rollback(MyApp.Repo, "20191106071140")'
=>
08:46:22.033 [error] GenServer #PID<0.207.0> terminating
** (RuntimeError) connect raised KeyError exception: key :database not found. The exception details are hidden, as they may contain sensitive data such as database credentials. You may set :show_sensitive_data_on_connection_error to true when starting your connection if you wish to see all of the details
(elixir) lib/keyword.ex:393: Keyword.fetch!/2
(postgrex) lib/postgrex/protocol.ex:92: Postgrex.Protocol.connect/1
(db_connection) lib/db_connection/connection.ex:69: DBConnection.Connection.connect/2
(connection) lib/connection.ex:622: Connection.enter_connect/5
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: nil
** (EXIT from #PID<0.163.0>) shutdown
I am trying to ensure I know how to deploy an application to a remote (Heroku, AWS) and have the application automatically migrate on every deploy but also have the option to run a command to roll back 1 step at a time.
I am not finding any information. The debugging above is first step in creating this migrate/rollback functionality on a remote server but testing on my local machine first.
The migrate/rollback code is taken directly from https://hexdocs.pm/phoenix/releases.html#ecto-migrations-and-custom-commands
Any help/direction would be greatly appreciated.
Thank you
In the first place, rpc call should succeed. Make sure you indeed have the migration in the question up before running my_app rpc. Note, that the second argument is the version to revert to, not the migration to revert.
Regarding the eval. One should start or at least load the application before any attempt to access its config. As per documentation:
You can start an application by calling Application.ensure_all_started/1. However, if for some reason you cannot start an application, maybe because it will run other services you do not want, you must at least load the application by calling Application.load/1. If you don't load the application, any attempt at reading its environment or configuration may fail. Note that if you start an application, it is automatically loaded before started.
For the migration to succeed, one needs Ecto aplication Ecto.Adapters.SQL.Application started and your application loaded (to access configs.)
That said, something like this should work.
def my_rollback(version) do
Application.load(:my_app)
Application.ensure_all_started(:ecto_sql)
Ecto.Migrator.with_repo(MyApp.Repo,
&Ecto.Migrator.run(&1, :down, to: version))
end
And call it as
./my_app eval 'MyApp.Release.my_rollback(20191106071140)'
Still, rpc should start the required applications out of the box (and it indeed does, according to the message you get back,) so I’d suggest you to triple-check the migration you are requesting to down is already up and you pass the proper version to downgrade to.
There were two issues here and thanks to #aleksei-matiushkin I got it working.
The first issue was not having Application.load(:my_app) in the function.
The second issue was that I was calling the rollback functions (both mine and #aleksei-matiushkin) as a string and not an int. Now I call it like: ./my_app eval 'MyApp.Release.my_rollback(20191106071140)'
The file now looks like this:
defmodule MyApp.Release do
#app :my_app
def migrate do
for repo <- repos() do
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
end
end
def rollback(repo, version) do
setup_for_rollback()
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
end
def my_rollback(version) do
setup_for_rollback()
rollback(MyApp.Repo, version)
end
defp setup_for_rollback() do
Application.load(#app)
Application.ensure_all_started(:ecto_sql)
end
defp repos do
Application.load(#app)
Application.fetch_env!(#app, :ecto_repos)
end
end
I am not sure if this is an idiomatic implementation. I did not have any issues excluding Application.ensure_all_started(:ecto_sql) but since it was recommended I guess I'll leave it in.

Mnesia in 2 separate IEx sessions on the same node

I'm learning Erlang's mnesia DB and trying to use it in Elixir app. According to documentation while creating a schema by default mnesia creates a directory with the name "Mnesia.#{node}". Here is the code for mnesia preparation:
:mnesia.create_schema([node])
:mnesia.start
:mnesia.create_table(User,
[
attributes: [:name, :email],
disc_copies: [node]
]
)
When I'm starting 2 separate IEx sessions and make some transactional queries it seems they are only happened in one of the session. I infer this from :mnesia.info output.
How can I synchronise them, or is it an expected behaviour?
When one starts a shell without an explicitly specified node name, it’s started as nonode#nohost:
$ iex
Erlang/OTP 19 [erts-8.1] [source-e7be63d] ...
Interactive Elixir (1.5.0-dev) ...
iex(1)> node()
:nonode#nohost
Mnesia itself is node-aware. So, you need to explicitly remote-connect to the node, that is aware of running mnesia. Let’s start a named shell:
$ iex --sname mnesia # the node name is arbitrary
iex(mnesia#localhost)1>
Now, let’s start the second shell and connect to the firstly started shell remotely. To do so, just start a shell, then press CtrlG to enter User switch command mode and type:
User switch command
--> r 'mnesia#localhost' 'Elixir.IEx'
--> c
Now you have two shells, connected to the same node, that handles a mnesia instance.
More info in the IEx docs on remote shells.

Looking for a convenient way to start and stop applications with docker-compose

For each of my projects, I have configured a docker development environment consisting of several containers. I often switch between projects. That requires stopping one set of containers and starting another. I currently do it like this:
$ cd project1
$ docker-compose stop
$ cd ../project2
$ docker-compose up -d
So I need to remember which application is currently running, cd into the directory where its docker-compose.yml is, stop it, then remember what other project I want to run, cd there and start it.
Is there a better way? Like a utility that remembers which multicontainer applications I have, can stop the currently running one and run another one without manual cding and docker-composeing?
(By the way, what's the correct term for a set of containers hosting parts of a single application?)
Hope docker-compose-ui will help you in managing applications.
I think the real problem here is this:
That requires stopping one set of containers and starting another.
You shouldn't need to stop one project to start another.
Instead of mapping to the same host ports I would not map any ports at all. Then use a script to lookup the IP of the container, and connect directly to that:
#!/bin/bash
cip=$(docker inspect -f '{{range $key, $value := .NetworkSettings.Networks}} {{ $value.IPAddress}} {{end}}' $1)
This will look up the container ip. Combine that with a command to open the url:
url=http://cip:8080/
xdg-open $url || open $url
All together this will let you run the application without having to map any host ports. When host ports don't exist, you don't have to stop other projects.
If you are ruby proven a bit, you can use scaffolding for this.
A barebone example using thread ( to start different docker-compose session without one process and then stop them all together )
require 'docker-compose'
threads = []
project_paths = %w(/project/path1 /project/path2 /project/path3 /project/path)
project_paths.each do |path|
threads.push Docker::Compose::Session.new(dir:compose_base_path1)
end
begin
threads.each do |thread|
thread.join
end
rescue SystemExit, Interrupt
threads.each do |thread|
thread.kill
end
rescue Exception => e
handle_exception e
end
source
It uses
docker-compose gem
threads
Just set project_paths to the folders of your projects. And if you want to end them all, use CTRL+c
You can of course go beyond that, using a daemon and try to start / stop some of them giving "names" and such, but i guess as a starting point for scaffolding, that should be enaugh

How can I build an Elixir escript that does not halt the Erlang VM after execution (like elixir --no-halt)

I have a program that starts the application and then adds (children) workers to a supervisor. Obviously after doing only that it has nothing more left to do and it halts (exits). So making it not halt the VM would allow the workers to work.
The only solution I have came up was to add:
IO.gets "Working... To finish hit <Enter>."
at the end...
I want to build an escript that after running will not halt the Erlang VM just like:
elixir --no-halt -S mix run --eval 'MyApp.CLI.m
ain(["some-arg"])'
or
mix run --no-halt --eval 'MyApp.CLI.m
ain(["some-arg1,some-arg2"])'
Is there a way to do this with escript?
Or should I use a different solution to pack and distribute my program that is actually more like a server/daemon than a command line tool?
A typical approach to packaging such systems is an OTP release. You can use exrm for that.
If for some reasons, you still want to use escript, you can just call :timer.sleep(:infinity) after you start all the applications and processes.
NOTE: Starting from Elixir 1.9
We can use System.no_halt(true) to allow script to never stop.
Here is simple script example:
defmodule Mix.Tasks.NoHalt do
use Mix.Task
def run(_) do
System.no_halt(true)
IO.puts("Never die!")
end
end

Optimizing Rails loading for maintenance scripts

I wrote a script that does maintenance tasks for a rails application. The script uses a class that uses models defined in the application. Just an example, let's say application defines model User, and my class (used within the script), sends messages to it, like User.find id.
I am looking for ways to optimize this script, because right now it has to load the application environment: require '../config/environment'. This takes ~15 seconds.
Had the script not use application codebase to do its job, I could have replaced model abstractions with raw SQL. But unfortunatly I can't do that because I would have to repeat the code in the script that is already present in the codebase. Not only would this violate DRY principle and require alot of work, the script would not be very maintainable, in case the model methods that I am using change.
I would like to hear ideas how to approach this problem. The script is not run from the application itself, but from the shell (with Capistrano for instance).
I hope I've described the problem clear enough. Thank you.
Could you write a little daemon that is in a read on a pipe (or named fifo, or unix domain socket, or, with more complexity, a tcp port) that accepts 'commands' that would be run on your database?
#!/usr/bin/ruby
require '../config/environment'
while (true) do
File.open("/tmp/fifo", "r") do |f|
f.each_line do |line|
case line
when "cleanup" then puts "clean!"
when "publish" then puts "published!"
else puts "invalid command, ignoring"
end
end
end
end
You could start this thing up with vixie cron's #reboot specifier, or you could run it via capistrano commands, or run it out of init or init scripts. Then you write your capistrano rules (that you have now) to simply echo commands into the fifo:
First,
mkfifo /tmp/fifo
In one terminal:
$ ./env.rb
In another terminal:
$ echo -n "cleanup" > /tmp/fifo
$ echo -n "publish" > /tmp/fifo
$ echo -n "go away" > /tmp/fifo
The output in the first terminal looks like this:
clean!
published!
invalid command, ignoring
You could make the matching as friendly (perhaps allow plain echo, rather than require echo -n as my example does) or unfriendly as you want. And the commands that get run can of course call into your model files to do their work.
Please make sure you choose a good location for your fifo -- /tmp/ is probably a bad place, as many distributions clear it on reboot. Also make sure you set the fifo owner and permission (chown and chmod) appropriately for your application -- you might not want to allow your Firefox's flash plugin to write to this file and command your database.

Resources