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

Fl4m3Ph03n1x
Background A few days ago I was listening to The future of Elixir from Elixir Talks, with Dave Thomas (@pragdave ) and Brian Mitchell. I...
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
WolfDan
After doing a port from a c++ library to my project in phoenix I’ve seen that I need a faster way to run this algorithm and I found this ...
New
sashaafm
Piggy backing a bit on @dvcrn topic BEAM optimization for functions with static return type?, I’ve been trying to understand in a deeper ...
New
fireproofsocks
This is more of a general question, but I’m wondering how other people in the community think about the pattern matching in function sign...
New
chuck
Let me start by stating an assumption: Phoenix is a great approach to building REST APIs. There are many reasons for this, but I will ass...
New
mbenatti
Following https://github.com/tbrand/which_is_the_fastest |&gt; https://raw.githubusercontent.com/tbrand/which_is_the_fastest/master/imgs...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
AstonJ
Can you believe the first professionally published Elixir book was published just 8 years ago? Since then I think we’ve seen more books f...
New
scouten
I’m looking for a host for the server part of a small (personal) side project that I’m working on. It’s currently written in Node.js and ...
New

Other popular topics Top

mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
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
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 53690 245
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

We're in Beta

About us Mission Statement