Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Running tenant migrations without Mix for apps deployed via mix release #85

Open
haizop opened this issue Aug 20, 2021 · 4 comments
Open

Comments

@haizop
Copy link

haizop commented Aug 20, 2021

Scenario

We deploy using releases, thus Mix is not available and we cannot migrate in production (or other server environments) using the Triplex Mix tasks. We run our public schema migrations via a similar Release module to the one recommended in the Phoenix documentation. To that basic structure, we added an additional migrate_tenants function:

defmodule MyApp.Release do
  @app :my_app

  def migrate do
    load_app()

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

  def migrate_tenants do
    load_app()

    for repo <- repos() do
      for tenant <- Triplex.all(repo) do
        Triplex.migrate(tenant, repo)
      end
    end
  end

  defp repos do
    Application.fetch_env!(@app, :ecto_repos)
  end

  defp load_app do
    Application.load(@app)
  end
end

Problem

The migrate_tenants function succeeds when run via a remote IEx session or rpc, but fails when run via eval, ie bin/my_app eval "MyApp.Release.migrate_tenants()". This is in contrast to the migrate function which succeeds when run via either remote IEx, rpc, or eval. The error observed is the following:

** (RuntimeError) could not lookup Ecto repo MyApp.Repo because it was not started or it does not exist
    lib/ecto/repo/registry.ex:19: Ecto.Repo.Registry.lookup/1
    lib/ecto/adapter.ex:127: Ecto.Adapter.lookup_meta/1
    lib/ecto/adapters/sql.ex:404: Ecto.Adapters.SQL.query/4
    lib/ecto/adapters/sql.ex:362: Ecto.Adapters.SQL.query!/4
    lib/triplex.ex:289: Triplex.all/1
    (myapp 0.1.0) lib/myapp/release.ex:21: anonymous fn/2 in MyApp.Release.migrate_tenants/0
    (elixir 1.11.4) lib/enum.ex:2193: Enum."-reduce/3-lists^foldl/2-0-"/3
    (myapp 0.1.0) lib/myapp/release.ex:20: MyApp.Release.migrate_tenants/0

Questions

  • Is it possible to migrate tenants using Triplex via eval on a release?
  • Is there a known root cause for this issue?
  • Is there a recommended approach for using Triplex with Mix releases in general?

Notes:

@haizop haizop changed the title Running Tenant Migrations without Mix for apps deployed via mix release Running tenant migrations without Mix for apps deployed via mix release Aug 20, 2021
@kelvinst
Copy link
Contributor

kelvinst commented Oct 4, 2021

Thanks for the report @haizop! Here are your answers:

Is it possible to migrate tenants using Triplex via eval on a release?

Should be possible, as it is basically doing pretty much the same that is done on your migrate function, with some extra options to run the migrations on the tenant prefixes.

Is there a known root cause for this issue?

By looking at the error, looks like the Repo is not started by the time Triplex.all is called, so it might be something to do with that. My guess is that your migrate function works because all the code that actually do something to DB is inside the Ecto.Migrator.with_repo, and by looking at that function you can see that they ensure the repo is started there. My suggestion is that you do the same manually like it's done there: https://github.com/elixir-ecto/ecto_sql/blob/master/lib/ecto/migrator.ex#L125

Is there a recommended approach for using Triplex with Mix releases in general?

There is nothing really specific to Triplex, the thing is that you kinda need to know some of the inner workings of ecto to actually make it work correctly, and that's an Ecto thing, not just specific to Triplex.

@haizop
Copy link
Author

haizop commented Oct 4, 2021

@kelvinst . Thank you for the reply.

I hear you that this is not a problem specific to Triplex, but given that Mix releases are a pretty standard method of deployment, do you think that some deployment guidelines should be added to Triplex documentation?

This is what I have working now:

defmodule MyApp.Release do
  @moduledoc """
  Used for executing DB release tasks when run in production without Mix
  installed.
  """

  alias Ecto.Migrator
  alias MyApp.Repo
  alias MyApp.Tenants

  @app :my_app

  def migrate_public_schema do
    load_app()

    {:ok, _, _} = Migrator.with_repo(Repo, &Migrator.run(&1, :up, all: true))
  end

  def migrate_tenant_schemas do
    load_app()

    Migrator.with_repo(Repo, fn repo ->
      Tenants.list_tenants()
      |> Enum.each(
        &Migrator.run(repo, tenant_migrations_path(), :up, all: true, prefix: &1.schema_name)
      )
    end)
  end

  def rollback_public_schema(version) do
    load_app()

    {:ok, _, _} = Migrator.with_repo(Repo, &Migrator.run(&1, :down, to: version))
  end

  def rollback_tenant_schemas(version) do
    load_app()

    Migrator.with_repo(Repo, fn repo ->
      Tenants.list_tenants()
      |> Enum.each(
        &Migrator.run(repo, tenant_migrations_path(), :down, to: version, prefix: &1.schema_name)
      )
    end)
  end

  defp tenant_migrations_path() do
    Triplex.migrations_path(Repo)
  end

  defp load_app do
    Application.load(@app)
  end
end

@kelvinst
Copy link
Contributor

kelvinst commented Oct 4, 2021

@haizop definitely! A PR with a guide for that is totally welcome. Will leave this issue open to remember to do exactly that.

@haizop
Copy link
Author

haizop commented Oct 4, 2021

@kelvinst Will get you a PR in the next couple of weeks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants