Ecto 2.0 SQL Sandbox Error on tests
Asked Answered
H

3

8

I recently upgraded my phoenix project to Ecto 2.0.2. I have some code that is using Task.Supervisor.async_nolink to make some updates to the db on its own thread. I am getting the following error when my tests run (only occurs on my tests)

[error] Postgrex.Protocol (#PID<0.XXX.0>) disconnected: **
(DBConnection.ConnectionError) owner #PID<0.XXX.0> exited while 
client #PID<0.XXX.0> is still running with: shutdown

Now I think I understand whats happening: The Ecto Sandbox connection pool is being checked back in before the db transaction is complete. According to the docs (at least the way I read them) the way to get around that stuff is to use a shared connection pool: Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()}) which I am doing. Unfortunately this is not working.

How do I set up my tests so that this error does not occur?

Hephzibah answered 12/7, 2016 at 17:38 Comment(0)
H
6

If anyone else encounters this, I got an answer back on this directly from the language author Jose Valim:

Yes, your understanding of the issue is correct. It is happening because the test process, the one who owns the connection, has exited but the Task is still using its connection. Using {:shared, self()} does not fix it because the test is still owning the connection, you are just sharing it implicitly.

The way to fix is to guarantee the Task has finished before the test exits. This can be done by calling Process.exit(task_pid, :kill). If you don't know the Task PID, you can call Task.Supervisor.which_children(NameOfYourTaskSupervisor) to return all PIDs which you then traverse and kill them. However, if you do this approach, the test cannot run concurrently (as you may kill tasks started by another test).

Hephzibah answered 9/8, 2016 at 16:55 Comment(2)
TLDR: there is no solution.Pegpega
Thanks for reporting on what was found. I used Jose's feedback to solve the problem nicely for me. So, that IS the solution.Whitehurst
C
5

I had the same problem today and I think I've found a possible solution with allows the tests to run concurrently.

I'm using the technique described here: http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/ to replace Task.Supervisor while running tests.

Instead of:

Task.Supervisor.async_nolink(TaskSupervisor, fn -> (...) end)

I'm doing:

@task_supervisor Application.get_env(:app, :task_supervisor) || Task.Supervisor
# ...
@task_supervisor.async_nolink(TaskSupervisor, fn -> (...) end)

and then I define TestTaskSupervisor

defmodule TestTaskSupervisor do
  def async_nolink(_, fun), do: fun.()
end

and add config :app, :task_supervisor, TestTaskSupervisor in config/test.exs.

This way, I'm sure that the task will run synchronously and finish before the test process.

Cobblestone answered 16/9, 2016 at 14:5 Comment(0)
R
-1

This problem is finally solved by Elixir v1.8.0 and db_connection v2.0.4. See https://twitter.com/plataformatec/status/1091300824251285504 and https://elixirforum.com/t/problem-asynchronizing-ecto-calls/19796/8

If you use versions of Elixir and DBConnection newer than those mentioned above, the test should work out of the box without any errors.

Rangy answered 9/2, 2019 at 22:30 Comment(1)
Using elixir 1.10.3 and db_connection 2.2.2. Still facing this issue.Reiko

© 2022 - 2024 — McMap. All rights reserved.