"owner #PID<...> exited with: shutdown" error during tests for my terminate/2 method of Phoenix.Channel

Hi, guys!

Looking for a help with testing terminate/2 callback in my Channel.

Test and setup looks like this:

setup do
  :ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo)
  Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()})

  {:ok, socket} = connect(UserSocket, %{token: "some_token"})
  {:ok, %{}, socket} = subscribe_and_join(socket, "some_channel", %{})

  %{socket: socket}
end

test "terminate/2", %{socket: socket} do
  # for avoiding "** (EXIT from #PID<...>) {:shutdown, :closed}"
  Process.unlink(socket.channel_pid)

  assert close(socket) == :ok
  # some additional asserts go here
end

In terminate/2 method I just call a helper module, let’s name it TerminationHandler.

def terminate(_reason, _socket) do
  TerminationHandler.call()
end

And call/0 method in TerminationHandler contains a DB query. It can look like this i.e

def call() do
  users = User |> where([u], u.type == "super") |> Repo.all # line where error appears
  # some extra logic goes here
end

This is the error that I get periodically (maybe once in 10 runs)

14:31:29.312 [error] GenServer #PID<0.1041.0> terminating
** (stop) exited in: GenServer.call(#PID<0.1040.0>, {:checkout, #Reference<0.3713952378.42205187.247763>, true, 60000}, 5000)
    ** (EXIT) shutdown: "owner #PID<0.1039.0> exited with: shutdown"
    (db_connection) lib/db_connection/ownership/proxy.ex:32: DBConnection.Ownership.Proxy.checkout/2
    (db_connection) lib/db_connection.ex:928: DBConnection.checkout/2
    (db_connection) lib/db_connection.ex:750: DBConnection.run/3
    (db_connection) lib/db_connection.ex:644: DBConnection.execute/4
    (ecto) lib/ecto/adapters/postgres/connection.ex:98: Ecto.Adapters.Postgres.Connection.execute/4
    (ecto) lib/ecto/adapters/sql.ex:256: Ecto.Adapters.SQL.sql_call/6
    (ecto) lib/ecto/adapters/sql.ex:436: Ecto.Adapters.SQL.execute_or_reset/7
    (ecto) lib/ecto/repo/queryable.ex:133: Ecto.Repo.Queryable.execute/5
    (ecto) lib/ecto/repo/queryable.ex:37: Ecto.Repo.Queryable.all/4
    (my_app) lib/my_app/helpers/termination_handler.ex:4: MyApp.Helpers.TerminationHandler.call/0
    (stdlib) gen_server.erl:673: :gen_server.try_terminate/3
    (stdlib) gen_server.erl:858: :gen_server.terminate/10
    (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: {:join, Phoenix.Channel.Server}

Would appreciate any responses regarding reasons of this error and possible ways to avoid it.

Terminate callbacks aren’t always called, the proper way would be for some other process that both owns that pid and monitors ‘your’ pid and properly closes it down when your pid dies, essentially another process becomes the terminator (and it can watch ‘many’ processes too).

Didn’t have a situation when terminate wasn’t executed and the cause of that error is still unclear to me. Why can it be that DB connection can’t be checked out and only sometimes? It’s somehow connected to the fact that socket is a separate process and some parent process terminates before the end of a test case making Ecto to fail