tiagodavi

tiagodavi

Cannot find ownership process for #PID<0.564.0>

It’s a little annoying testing async code in eixir…

Is it possible to run async tests with database by manually controlling my connections?

I have a piece of code that does:

caller = self()
Task.Supervisor.start_child(X.TaskSupervisor, fn ->
Ecto.Adapters.SQL.Sandbox.allow(X.Repo, caller, self())

it returns: cannot find ownership process for pid…

I put

setup do
# Explicitly get a connection before each test
:ok = Ecto.Adapters.SQL.Sandbox.checkout(X.Repo)
end

it returns: {:already, :owner}
and still: cannot find ownership process for pid…

I want to run async: true.

Should I always run async: false with databases?

Marked As Solved

ityonemo

ityonemo

This works for me:

  test "async databases" do
    {:ok, sup} = Task.Supervisor.start_link()
    test_pid = self()
    data = DB.Table.insert_new()
    DB.Table.all() |> IO.inspect  # has the new item
    Task.Supervisor.start_child(sup, fn ->
      DB.Table.all() |> IO.inspect  # sees the same stuff
      # we're finished.  If you don't do this parent test pid closes and takes the db checkout with it
      send(test_pid, :done)
    end)

    #keep the db checkout alive till it's done
    receive do :done -> :ok end
  end

Also Liked

ityonemo

ityonemo

Correct. GenServers don’t bind $callers. This is deliberate. I’m not saying it’s always a bad idea (because I do something similar for a real reason in prod [see note]), but if you have a GenServer calling a database, there’s a good chance you’re violating the basic good-practices principle of functional-core OTP design:

https://hexdocs.pm/elixir/GenServer.html#module-when-not-to-use-a-genserver

Use processes only to model runtime properties, such as mutable state, concurrency and failures, never for code organization.

If you’re using a GenServer to call a database there is a really good chance you are trying to use the GenServer to organize your code. If you truly have transient GenServers, after you spin them up in your task, 1) your test should be able to find it and 2) you should be then able to run Process.put(:“$callers”, [test_pid]) inside the GenServer, or (better Sandbox.allow), then your genserver will see the database.

If really really need some sort of Global Genserver with a name, then it’s Global. You can’t run async tests on it, because you have shared async state, and that’s that. I think that honestly most use cases don’t need this, so you may be able to rearchitect your system.

[note] I did the Process.put(:“$callers”) thing, and took the hit on convenience because it was important. I think elixir doing this is a great thing, because the roadblock made me stop and think, “do I really need this?”.

ityonemo

ityonemo

In your code you’re spawning a task that immediately dies after attempting to obtain ownership. Tasks also get bound into the caller chain anyways, so that’s why it says “already owner” when you run the allowance after you activate the repo.

You are probably attempting to access the database from some thread “out there” somewhere. You need to set the allowance on the thread that’s calling the database (not a random thread that is just going to die anyways). Find the pid at the site of the database repo call (maybe inspect it). If you can’t figure out how to communicate that pid back to the test, it may be that you have architecture your system in a way that makes it difficult to test. Imo, those systems will also be hard to maintain in the long run. In which case you have two options: roll with it, or rearchitect your system.

bruteforcecat

bruteforcecat

Are you sure you are using manual mode for Sandbox?

tiagodavi

tiagodavi

Thank you guys!

I was able to solve it by thinking another approach. I decided to “await for messages” synchronously by using a recursive function. Since this functions runs inside a “Task” it has access to the database automatically… and it’s working.

defp consume(channel, queue, attempts \\ 1) do
    if attempts <= 30 do 
      AMQP.Basic.get(channel, queue)
      |> case do
        {:empty, _} ->
          :timer.seconds(10 * attempts)
          |> Process.sleep()
          consume(channel, queue, attempts + 1)
        {:ok, payload, _meta} ->
          AMQP.Queue.delete(channel, queue)
          AMQP.Channel.close(channel)
         
          similarities = Jason.decode!(payload)
          update_similarities(similarities)
        _ -> 
          {:error, "Rabbit is crazy"}
      end
    else 
      {:error, "Rabbit is offline"}
    end
  end
ityonemo

ityonemo

This code looks much nicer and easier to rationalize!!

Where Next?

Popular in Questions Top

LegitStack
I’m hoping you guys can give me some general advice and perhaps code examples if you’re feeling up to it. I’m very interested in Elixir,...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
sacepums
Hey guys. I'm new to elixir and im really stocked about it. But I ran into a bit of problem - I need to convert a date sting, for examp...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
chewm
Hi guys, nice to meet you to the whole forum, I’m new here, I’m trying to configure visual studio code for elixir, right now the intellis...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New

Other popular topics Top

lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

We're in Beta

About us Mission Statement