amnu3387

amnu3387

Poolboy substitute?

Hi, I’ve written a pooler using gen_statem, similar to poolboy but with a few differences, more control on how it can be configured (default workers, max workers (similar to overflow in poolboy) and waiting queue lenght), some more control on checking out the worker (immediate overload response, only timeout, unbound, not swallowing timeouts).

I’ve ran some tests with it and poolboy and the results are promising. What I’m wondering is if there’s any other more efficient poolers out there? Perhaps nif based? I’m asking because if not I would like to rewrite it in erlang (it’s mostly compatible with only some keyword lists for initialisation and naming which can be swapped for something else) and publish it publicly (and because poolboy is used in some public elixir libs if it was a better option it would be, well, better?).

They have slightly different semantics on how they control, the things but running poolboy with no “block”, and an :infinity timeout, against the unbound waiting queue, and 5_000 timeout, both with 100 workers, not overflow, results in this:

Interactive Elixir (1.9.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Queuer.Pool.test_queuer()
:ok
Total ok: 40000 - Total failed: 0
time: 1.13
iex(2)> Queuer.Pool.test_poolboy()
:ok
Total ok: 40000 - Total failed: 0
time: 41.639

The results are consistent across all range of workloads, workers, etc.

The tests are basically:

@total_reqs 20000
  
  def test_queuer(max \\ @total_reqs)  do
    pid = Process.whereis(__MODULE__)
    time = :erlang.system_time(:millisecond)
    receiver = spawn(__MODULE__, :keep_track, [time, 0, 0])
    feed_test_queuer(pid, receiver, 0, max)
    feed_test_queuer(pid, receiver, 0, max)
  end

  def feed_test_queuer(pid, receiver, count, max) when count < max do
    spawn(fn() ->
      case __MODULE__.checkout(pid, 5000, false) do
        {:ok, w_pid} ->
          :ok = GenServer.call(w_pid, :test)
          send(receiver, :ok)
          __MODULE__.checkin(pid, w_pid)
        error ->
          send(receiver, :failed)
          :error
      end
    end)
    feed_test_queuer(pid, receiver, count + 1, max)
  end

  def feed_test_queuer(_, _, _, _), do: :ok

  def test_poolboy(max \\ @total_reqs) do
    pid = Process.whereis(:worker)
    time = :erlang.system_time(:millisecond)
    receiver = spawn(__MODULE__, :keep_track, [time, 0, 0])
    feed_test_poolboy(pid, receiver, 0, max)
    feed_test_poolboy(pid, receiver, 0, max)
  end

  def feed_test_poolboy(pid, receiver, count, max) when count < max do
    spawn(fn() ->
      case :poolboy.checkout(pid, true, :infinity) do
        :full ->
          send(receiver, :failed)
          :error
        w_pid ->
          :ok = GenServer.call(w_pid, :test)
          send(receiver, :ok)
          :poolboy.checkin(pid, w_pid)
      end
    end)
    feed_test_poolboy(pid, receiver, count + 1, max)
  end

  def feed_test_poolboy(_, _, _, _), do: :ok
 
   def keep_track(time, c, f) when (c + f) == @total_reqs do
    time_now = :erlang.system_time(:millisecond)
    IO.puts("Total ok: #{c} - Total failed: #{f}")
    IO.inspect(((time_now - time) / 1000), label: "time")
  end
  
  def keep_track(time, count, failed) do
    receive do
      :ok -> keep_track(time, count + 1, failed)
      :failed -> keep_track(time, count, failed + 1)
    after
      10_000 ->
        :ok
    end
  end

In short, checkout a worker, call it for :ok, send a message to the “tracker”.
The worker module is:

defmodule WorkerTest do
  use GenServer

   def start_link do
    GenServer.start_link(__MODULE__, nil, [])
  end
  
  def start_link(_) do
    GenServer.start_link(__MODULE__, nil, [])
  end

  def init(_) do
    {:ok, nil}
  end

  def handle_call(:test, _from, state) do
    {:reply, :ok, state}
  end
end

(it has two start_links, because my queuer is passing 0 args to it)
Can you see something wrong with the way I’m testing both implementations?

First Post!

hauleth

hauleth

Have you read @ferd’s article about handling overload?

Where Next?

Popular in Discussions Top

PragTob
Hello everyone, I know we had quite some threads (read through lots of them) about background job processing but it remains a hotly deba...
New
Donovan
Hello everyone, I’m so glad to have discovered this awesome community. Thanks for creating it! This is my second post, and apologies for...
New
pillaiindu
In django there is a cache framework backed by memcached. Rails also puts a lot of emphasis on caching, and even the idea of russian-doll...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
mmmrrr
Just saw that dhh announced https://hotwire.dev/ Is it just me or is this essentially live view? :smiley: Although I like the “iFrame-e...
New
lucaong
Hello Elixir and Nerves community, I have been working for a while on an open-source embedded key-value database for Elixir, that I call...
230 13924 124
New
IVR
Hi all, I’ve seen a number of related threads in the past, but I’d still be very curious to hear an up-to-date opinion on this topic. I...
New
sergio
There’s a new TIOBE index report that came out that shows Elixir is still not in the top 50 used languages. It also goes on to call Elix...
New
RudManusachi
What configs will make sense to put to runtime.exs? – A bit of how I configure apps: I have generic configs in config/config.exs, dev...
New
Owens
Hello all, I am developing a new mobile app with Flutter frontend and Phoenix backend. The mobile app has real-time task management and c...
New

Other popular topics Top

marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
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
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
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
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New

We're in Beta

About us Mission Statement