Ecto 2.0 SQL 沙盒测试错误
Ecto 2.0 SQL Sandbox Error on tests
我最近将我的 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 连接池被重新签入。根据文档(至少我阅读它们的方式),解决这些问题的方法是使用共享连接池:Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()})
我正在做的。不幸的是,这是行不通的。
如何设置我的测试才不会发生此错误?
如果其他人遇到这个问题,我直接从语言作者 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).
我今天遇到了同样的问题,我想我已经找到了允许测试同时 运行 的可能解决方案。
我正在使用此处描述的技术: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/test.exs
中添加config :app, :task_supervisor, TestTaskSupervisor
。
这样,我确定任务将 运行 同步并在测试过程之前完成。
Elixir v1.8.0 和 db_connection
v2.0.4 终于解决了这个问题。参见 https://twitter.com/plataformatec/status/1091300824251285504 and https://elixirforum.com/t/problem-asynchronizing-ecto-calls/19796/8
如果您使用的 Elixir 和 DBConnection 版本比上述版本更新,则测试应该开箱即用,不会出现任何错误。
我最近将我的 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 连接池被重新签入。根据文档(至少我阅读它们的方式),解决这些问题的方法是使用共享连接池:Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()})
我正在做的。不幸的是,这是行不通的。
如何设置我的测试才不会发生此错误?
如果其他人遇到这个问题,我直接从语言作者 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).
我今天遇到了同样的问题,我想我已经找到了允许测试同时 运行 的可能解决方案。
我正在使用此处描述的技术: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/test.exs
中添加config :app, :task_supervisor, TestTaskSupervisor
。
这样,我确定任务将 运行 同步并在测试过程之前完成。
Elixir v1.8.0 和 db_connection
v2.0.4 终于解决了这个问题。参见 https://twitter.com/plataformatec/status/1091300824251285504 and https://elixirforum.com/t/problem-asynchronizing-ecto-calls/19796/8
如果您使用的 Elixir 和 DBConnection 版本比上述版本更新,则测试应该开箱即用,不会出现任何错误。