trisolaran

trisolaran

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 :slight_smile:

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?

Showing Posts 1 to 6

LostKobrakai

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.

trisolaran

trisolaran OP

Thanks @LostKobrakai! Very interesting. I guess it must be using the mechanism you describe, it’s just that I can’t find where this happens in the code.

trisolaran

trisolaran OP

Thanks. This explains it. So this is a generic mechanism, not specific to LV. Whenever the sandbox can track the caller using the $callers key in the process dictionary, it will try to use a DB connection from the caller, and no explicit allowances are needed. So this works whenever I start a new process using Task, Task.Supervisor, or Supervisor (I just tested with Task.async and it works).

I have to say I was completely in the dark about this one. The sandbox docs never mention it (Ecto.Adapters.SQL.Sandbox — Ecto SQL v3.14.0). I think they should. Reading the docs I would think that you need allowances or shared DB connections whenever you’re dealing with subprocesses, but clearly this is not necessary if you are using supervised tasks and that’s very relevant information.

trisolaran

trisolaran OP

I mean, how did you find about this mechanism @LostKobrakai ? Just poking around in the code, or did you read about it somewhere?

LostKobrakai

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.

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New
dillonoconnor
Is there any way to avoid the Hologram compiler running when using iex? It seems like the front-end code could potentially be disregarded...
New
thiagogsr
** (ArgumentError) expected :max_attempts to be a positive integer, got: {:@, [line: 10, column: 19], [{:max_attempts, [line: 10, column:...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews