Ecto 2.0 SQL Sandbox Ошибка на тестах
Я недавно обновил свой проект Phoenix до Ecto 2.0.2. У меня есть код, который использует Task.Supervisor.async_nolink
сделать некоторые обновления БД в своем собственном потоке. При выполнении моих тестов я получаю следующую ошибку (происходит только на моих тестах)
[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
Теперь я думаю, что понимаю, что происходит: пул соединений Ecto Sandbox проверяется до завершения транзакции db. Согласно документам (по крайней мере, так, как я их читаю), способ обойти это - использовать общий пул соединений: Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()})
что я делаю. К сожалению, это не работает.
Как настроить тесты, чтобы эта ошибка не возникала?
2 ответа
Если кто-то еще сталкивался с этим, я получил ответ на этот вопрос непосредственно от автора языка Хосе Валима:
Да, вы понимаете проблему правильно. Это происходит потому, что тестовый процесс, тот, кто владеет соединением, завершился, но Задача все еще использует свое соединение. Использование {:shared, self()} не исправляет это, потому что тест все еще владеет соединением, вы просто делитесь им неявно.
Способ устранения - гарантировать, что Задание завершено до завершения теста. Это можно сделать, вызвав Process.exit(task_pid,:kill). Если вы не знаете PID задачи, вы можете вызвать Task.Supervisor.which_children(NameOfYourTaskSupervisor), чтобы вернуть все PID, которые вы затем пройдете, и убить их. Однако, если вы используете этот подход, тест не может выполняться одновременно (так как вы можете убить задачи, запущенные другим тестом).
У меня была такая же проблема сегодня, и я думаю, что нашел возможное решение, позволяющее тестам выполняться одновременно.
Я использую методику, описанную здесь: http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/ чтобы заменить Task.Supervisor во время выполнения тестов.
Вместо:
Task.Supervisor.async_nolink(TaskSupervisor, fn -> (...) end)
Я делаю:
@task_supervisor Application.get_env(:app, :task_supervisor) || Task.Supervisor
# ...
@task_supervisor.async_nolink(TaskSupervisor, fn -> (...) end)
а потом я определяю TestTaskSupervisor
defmodule TestTaskSupervisor do
def async_nolink(_, fun), do: fun.()
end
и добавить config :app, :task_supervisor, TestTaskSupervisor
в config/test.exs
,
Таким образом, я уверен, что задача будет выполняться синхронно и завершится до начала процесса тестирования.
Эта проблема, наконец, решена с помощью Elixir v1.8.0 и db_connection
v2.0.4. См. https://twitter.com/plataformatec/status/1091300824251285504 и https://elixirforum.com/t/problem-asynchronizing-ecto-calls/19796/8
Если вы используете версии Elixir и DBConnection, более новые, чем упомянутые выше, тест должен работать без каких-либо ошибок.