4

If you need access to DB for you mix task - you needed to start your ecto repo manually. While we worked with ecto 2 we had ensure_started function.

So custom CLI task with ecto 2 looks like

defmodule App.Task do
  use Mix.Task
  import Mix.Ecto

  def run(args) do
    repos = parse_repo(args)

    Enum.each repos, fn repo ->
      ensure_repo(repo, args)
      ensure_started(repo, []) # was deleted from ecto 3

ensure_started has been deleted from ecto 3. I tried a few approaches but they didn't work for me. Does anybody know a workaround for ecto 3?

Updated

as @starbelly mentioned ensure_started has migrated to Mix.EctoSQL so we need to add extra import Mix.EctoSQL to make it work

defmodule App.Cli.Task do
  import Mix.Ecto
  import Mix.EctoSQL

  def start_ecto do
    repos = parse_repo([])

    Enum.each(repos, fn repo ->
      ensure_repo(repo, [])
      {:ok, _pid, _apps} = ensure_started(repo, [])
    end)
  end
end
radzserg
  • 1,258
  • 1
  • 13
  • 22
  • 1
    Would not [`Application.ensure_all_started/2`](https://hexdocs.pm/elixir/master/Application.html#ensure_all_started/2) help? Like `Application.ensure_all_started(Ecto)`? – Aleksei Matiushkin Dec 12 '18 at 12:06
  • I tried it but no luck {:ok, apps} = Application.ensure_all_started(:ecto_sql) IO.inpsect apps [:telemetry, :connection, :db_connection, :decimal, :ecto, :ecto_sql] But as soon as I need to make a query ** (RuntimeError) could not lookup Smd.Repo because it was not started or it does not exist (ecto) lib/ecto/repo/registry.ex:18: Ecto.Repo.Registry.lookup/1 (ecto) lib/ecto/repo/queryable.ex:131: Ecto.Repo.Queryable.execute/4 (ecto) lib/ecto/repo/queryable.ex:18: Ecto.Repo.Queryable.all/3 lib/smd/app/search/sync_es.ex:76: Mix.Tasks.App.SyncEs.sync_all_pages/4 – radzserg Dec 13 '18 at 03:16

4 Answers4

3

The function you're trying to import and call has been moved into ecto_sql. You should import Mix.EctoSQL for ensure_started/2

Example:

defmodule Mix.Tasks.Friends.Hello do
  use Mix.Task
  import Mix.Ecto
  import Mix.EctoSQL

  def run(args) do
    repos = parse_repo(args)

    Enum.each(repos, fn repo ->
      ensure_repo(repo, args)
      {:ok, _pid, _apps} = ensure_started(repo, [])
      Friends.Repo.all(Friends.Person)
    end)
  end
end
starbelly
  • 254
  • 1
  • 4
3

None of these approaches will work anymore in the latest version of ecto_sql (3.1.2) because Mix.EctoSQL.ensure_started/2 was a private function, not meant for use outside of Ecto.

As per Jose, the correct approach is to use

Mix.Task.run("app.start")

So the example @starbelly shared would be modified to be

defmodule Mix.Tasks.Friends.Hello do
  use Mix.Task

  def run(args) do
    Mix.Task.run("app.start")
    repos = parse_repo(args)

    Enum.each(repos, fn repo ->
      repo.all(Friends.Person)
    end)
  end
end

The big drawback to this approach is it will start your entire application, including any background processes you have. As a result, you may need to modify your application.ex file to not start those if started by a mix task.

  • how would you achieve that ? (modifying application.ex to not start something is started by a mix task) – Robin Komiwes Dec 21 '19 at 10:21
  • 1
    @RobinKomiwes there are a few ways to do it, but one of the simplest would be to do something like `Application.put_env(:your_app_name, :process_type, :mix_task)` at the top of your `run` function in the mix task. Then in your `application.ex` file, you could limit the processes you tell the `Supervisor` to start up to only include the `Repo` process based on that `:process_type` value – Dennis Beatty Dec 24 '19 at 22:03
2

As Dennis pointed out Mix.EctoSQL.ensure_started/2 is a private function since ecto_sql 3.1.2, so we can't use it anymore.

Assuming you have an OTP application defined, which this mix task is part of, what you could do is

defmodule Mix.Tasks.Friends.Hello do
  use Mix.Task

  def run(args) do
    {:ok, _started} = Application.ensure_all_started(:your_otp_app_name)
    repos = parse_repo(args)

    Enum.each(repos, fn repo ->
      repo.all(Friends.Person)
    end)
  end
end

Essentially start the whole OTP application, so that all the dependencies (including the repos) would be started as well.

wiser
  • 179
  • 1
  • 7
1

For Ecto 3.x use Repo.start_link() or Mix.Task.run('app.start')

This question/answer might help. How to get data from Ecto in a custom mix task

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 22 '21 at 18:15
  • Recommended by whom? – Andrew Oct 23 '21 at 10:04
  • added the reference @Andrew. Should've included earlier. (as per Jose Valim) – Tahseen Siddiqui Oct 25 '21 at 16:07