Loading...
Loading...

Reputation: 913

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:[email protected]/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

Upvotes: 1

Views: 999

Answers (2)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121010

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.

Upvotes: 3

Loading...
Loading...

Reputation: 913

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.

Upvotes: 0

Related Questions