trisolaran
How are DB connections passed from the test process to the liveview process when using the ecto sandbox?
Hello!
I noticed that if I have a LV test that uses the Ecto sandbox in manual mode, the LV process in the test has “magically” access to the sandboxed DB connection created by the test process. This is really amazing but I don’t understand how it works ![]()
Simplified example:
Liveview:
defmodule MyAppWeb.LiveView do
@moduledoc false
import Ecto.Query
use MyAppWeb, :live_view
@impl true
def mount(_params, _session, socket) do
connect_params = get_connect_params(socket)
IO.inspect(connect_params, label: "connect_params")
IO.inspect(self(), label: "I'm the LV")
Repo.all(MyApp.User) |> length() |> IO.inspect(label: "users")
{:ok, socket}
end
end
Test setup:
defmodule MyAppWeb.ConnCase do
...
setup tags do
Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, :manual)
:ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo)
{:ok, conn: Phoenix.ConnTest.build_conn()}
end
end
The actual test:
defmodule MyAppWeb.LiveViewTest do
use MyAppWeb.ConnCase
test "just to prove a point", %{conn: conn} do
insert_test_records_in_user_table(20)
IO.inspect(self(), label: "I'm the test process")
{:ok, view, html} = live(conn, "/live_view")
end
end
Output:
1 I'm the test process: #PID<0.869.0>
2 connect_params: nil
3 I'm the LV: #PID<0.869.0>
4 entries: 20
5 connect_params: %{"_mounts" => 0}
6 I'm the LV: #PID<0.873.0>
7 entries: 20
Lines 2-4 come from the static LV mount, and lines 5-7 from the connected one. As you can see, the static mount happens in the same process as the test’s, while the connected mount happens in a different process. So far nothing strange. What I don’t understand is how the connected LV can see the test records inserted by the test process, even though I didn’t change the mode of the sandbox to shared or call Ecto.Adapters.SQL.Sandbox.allow. I would have expected the DB access from the connected LV process to fail with the error:
** (DBConnection.OwnershipError) cannot find ownership process for #PID<0.873.0>
The only explanation I can think of is that the sandboxed DB connection is somehow passed over to the LV process when this latter is spawned, by calling Ecto.Adapters.SQL.Sandbox.allow somewhere. But I couldn’t find where this happens in the code.
Can someone please clarify this for me?
Marked As Solved
LostKobrakai
It’s passed into the clientproxy phoenix_live_view/lib/phoenix_live_view/test/client_proxy.ex at main · phoenixframework/phoenix_live_view · GitHub and used here: phoenix_live_view/lib/phoenix_live_view/channel.ex at 112b1446b7962285d699bce6d187139de71b9ffa · phoenixframework/phoenix_live_view · GitHub
Also Liked
LostKobrakai
There we‘re some blogposts around the introduction to the functionality for Tasks, which is by now a lot of elixir versions back. While ecto sandbox piggibacks on this, it‘s afaik meant to be a much more general feature (hence it‘s inclusion in elixir core). But it should probably still be mentioned in the docs that processes supporting $caller keys might get automatic concurrent support with the sandbox.
LostKobrakai
The sandbox uses the mechanic described here to track the caller (aka the test process) for a LV: Task — Elixir v1.12.3
With that pid as key it can fetch the connection checked out by that process.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









