I need to run `mix ecto.reset` in production

My dev environment is my MacBook and my prod environment is on Fly with a Postgres database I have configured via Fly.io.

I cannot run mix tasks on my Fly.io setup because it is working with releases.

I have a lib/myapp/release.ex file which runs Ecto migrations for me when I deploy to prod.

But I’m still in heavy learning/development mode overall, so my prod environment is not really production and I have no users – it is better thought of as a staging environment.

I’m constantly refactoring my schemas as I learn and experiment, so in development I constantly run mix ecto.reset to drop everything in my database and re-run my migrations from scratch.

But in my prod environment I cannot run mix ecto.reset. I could just add a migration that drops everything, but I’d rather not end up with hundreds of migrations before arriving at my final starting point.

So how can I add code to my release.ex module to just drop everything and start from scratch every time I deploy to production? Once I’m ready to actually keep my data around and get some users I’ll obviously remove the code that drops all the data every time I deploy.

Hi @tadasajon! You are in the right direction, you can use the same technique recommended for running migrations:

defmodule MyApp.Release do
  def greet(), do: IO.puts "Hello World"
end

And then you can eval the code:

_build/prod/rel/my_app/bin/my_app eval "MyApp.Release.greet"

You can check the code inside those ecto tasks for more information on how to do it programmatically for you app release:

TLDR; Take a look at Ecto.Adapters.Postgres.storage_down and Ecto.Adapters.Postgres.storage_up functions. You can see an example of how to do it in the test helper of this lib I’m working on.

2 Likes

This was super-helpful @thiagomajesk. Following your example, I was able to add the following function to lib/myapp/release.ex:

  def reset do
    load_app()
    Ecto.Adapters.Postgres.storage_down(Myapp.Repo.config())
    Ecto.Adapters.Postgres.storage_up(Myapp.Repo.config())
    migrate()
  end

and I have adjusted my fly.toml file to include these lines:

[deploy]
  release_command = "/app/bin/myapp eval Myapp.Release.reset"

But I’m unfortunately still hitting a snag. When I deploy my app the reset function runs as expected, but I get the following error:

{:error, "ERROR 55006 (object_in_use) database \"myapp\" is being accessed by other users\n\nThere are 11 other sessions using the database."}

I guess this is because I already have a previous version of my app up and running and it is holding some database connections.

I thought I could solve this by running fly suspend and bringing my app down for a few minutes, but after running fly suspend I am unable to run fly deploy, since I get the error:

Error App 'myapp' is currently suspended. Resume before deploying.

I’ll update here if I come up with a solution.

Rather than dropping the database, could you just migrate down? SSH into your box, and run something like Ecto.Migrator.run(MyApp.Repo, :down, all: true)

Then on your release, you can just migrate back up with your new migrations, or write a function to migrate them all down and back up.

2 Likes

If this is for development, why don’t you destroy the whole instance and create a new one? Doesn’t Fly have tools to do that easily?

1 Like

That’s basically the solution I’m coming around to – I’ll just have to figure out how to do all that programmatically.

Hey @tadasajon - did you ever figure out a solution that doesn’t involve destroying the database?

1 Like

Nope – I ended up writing a script that would just drop the entire DB and start over

1 Like

just followed your advice, it seems to get the job done:

  def migrate do
    load_app()

    for repo <- repos() do
      {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, all: true))
      {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
    end
  end

then ran fly deploy ...., i have to keep in mind to remove that 1 line before i commit it.