Ecto Sandbox automatically allow via caller?

** (DBConnection.OwnershipError) cannot find ownership process for #PID<0.955.0>.

When using ownership, you must manage connections in one
of the four ways:

* By explicitly checking out a connection
* By explicitly allowing a spawned process
* By running the pool in shared mode
* By using :caller option with allowed process

I am interested in that forth option, but I didn’t find any references to it in the documentation. I am hoping that it is an option to automatically allow child processes. The Task.Supervisor task process that triggered this error message has the original exunit test pid in its $callers list.

How do I use the :caller option? What does it do?

The caller tracking mechanism is described here: Task — Elixir v1.14.2

You seem to already know how it works, but in a nutshell: Task and Task.Supervisor will make sure that the caller of spawned processes is added to a list stored under the :"$callers" key in the process dictionary.

When running in manual mode, the Ecto sandbox exploits this mechanism to search for checked out connections: if one of the processes in the list of callers is the owner of a checked out connection, then the current process is automatically allowed to use the connection. This makes it possible to write a test like this:

test "share db connection using caller tracking" do 
      :ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)

      Repo.insert!(%User{id: 1})
      
      task = Task.Supervisor.async(MyTaskSupervisor, fn -> 
         assert Repo.exists?(where(User, id: 1))
      end)

      Task.await(task)
end

Without caller tracking, the spawned task wouldn’t be able to find a checked out connection and would raise. To make it work, you would have to either use explicit allowances or run the test in shared mode (and thus give up on concurrency).

The Task.Supervisor task process that triggered this error message has the original exunit test pid in its $callers list.

If the process executing the task has the test process’s pid in its callers list, then it should be able to find the checked out connection if the test process checked out a connection or is allowed to use one. Are you sure you checked out one in the test process?

but I didn’t find any references to it in the documentation

yup, it’s undocumented, and I think it should be.

2 Likes

Ok, what I described above is the caller lookup mechanism, but it’s not what the error message you saw is referring to. There is indeed a :caller option you can use on checkout. Both caller lookup and caller option are described here: DBConnection.Ownership — db_connection v2.4.3

(so yes, they’re documented, although the documentation is kind of hard to find)