general-CbIC
Hi ![]()
Poolex is an Elixir library for managing pools of workers.
In short, this is a poolboy written in Elixir.
When I started this project, I had the following goals:
- To solve the problem of missing documentation of public interfaces and “official” library usage examples.
- To bring this library back to life.
poolboyis not actively maintained. Even ifpoolboyis written perfectly, there may be a chance of incompatible OTP changes in the future or the appearance of new features we’d like to use. - Try to rewrite this library in Elixir. It’s not a problem, but I’d like to use Elixir dependencies when I’m writing in Elixir.
- To add the ability to use different strategies for getting a worker. I think a developer may have more needs than just choosing a
LIFO/FIFO. So I added the ability to describe and use implementations for operating withworkerandcallerprocess queues.
Some project links:
Trending in Announcing
Hey everyone!
Req is an HTTP client for Elixir that I’ve been working on for quite some time. There is already a lot of HTTP clients out...
New
Samly can be used to enable SAML 2.0 Single Sign On in a Plug/Phoenix application.
This library uses Erlang esaml to provide
plug enabl...
New
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries.
offset-based pagination with...
New
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New
Hobbes is a low-level distributed database for the Elixir programming language.
Hobbes provides a simple, safe, and scalable storage lay...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Hi all!
I want to present a small library which provides a mix task for generating an Entity-Relationship Diagram for Ecto schemas.
You...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
This showed up on my feed.. anyone heard of it? Just hype?
Ox Alpha is a reasoning model designed for coding, sustained ag...
New
It’s not that it’s vocabulary is too advanced. It’s something worse.
I get lost trying to follow even a paragraph written by Claude. It’...
New
Today we’re releasing Oban for Python. Not an Oban client in Python. Not a pythonx wrapper embedded in Elixir. Nope, it’s a fully operati...
New
@hugobarauna, Dr. Dimitrios Koutmos (my brother) and I (Alex Koutmos) have been hard at work on writing a book on how you can use Elixir ...
New
Introductory paragraph
I’ll be looking for a keen junior or someone that has a couple of years experience in the real world (so you’ve be...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #blog-post
- #ai
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming











Showing Posts 11 to 20- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
Asd
Hi, nice library, I’ve read the code and I have several questions:
pool_idinstead of more commonnamefor name registration?poolboyuses high priority for the pool owner genserver process. Why didn’t you implement the same here?Agentfor monitor references storage. It seems that it is redundant and just decreases the performance of the whole pool (I am preparing a PR with change to plain map).Project.Private.Modulenaming schema? It is the first time I see something like this. It is strange because thesePrivatemodules are exposed in documentation which declares that they can be used by developer (which means opposite ofPrivate)monitor_callerfunction which spawns a monitoring process for everyruncall what goes against the idea of having a pool of processes in the first places (since you end up spawning process for every call anyways). I’d suggesting moving caller monitoring to thehandle_call get_idle_workerlogic and not removing the monitor until the worker is released. Overall, following checkout/checkin pattern would solve the problem. Plus, these processes will live forever while the caller is alive. Consider some long-living process (for example very common pattern of GenServer which executes some command periodically) which calls this pool frequently. These monitoring processes would pile up until all memory is exhausted which is essentially a memory leak and will result in the whole BEAM shutting down.MatchErrorif some extra worker fails to start. It makes this pool inapplicable for usage in environments where workers connect to external services (like databases or HTTP services which are most pooling use-cases) which can be unavailable or return 422 for example.trap_exitflagAlso I’d like to mention that this approach of
BusyWorkersandIdleWorkersmodule which manage the same structure is uncommon (I personally see it for the first time) but very nice to read and it makes it really easy to follow the algorithm.I’ve found that you work in Авиасейлс and there’s a chance that you use this library in production. If you want an expert review of your other solutions, codebase and development practices to find more issues like I did just now, please leave me the message, my rates are low.
general-CbIC
Hi, @Asd!
)
(@hst337 ?
First of all, thank you very much for reading the code and describing several problems you found! Writing a project without a code review was quite tricky, and I no longer saw the issues you wrote about.
Initially, I used only
atom()as the first parameter since I did not see the need to complicate it. I hadn’t thought about using Registry, and it turns out that in my years of working on Elixir, I’ve never had to use anything other than atoms. In general, when a pool was intended to have a unique atom as its identifier,pool_idwas an appropriate name. Most likely, I made a mistake by not changing the name of this option when supporting theGenServer.name()type. But I don’t see any great criticality in this. I will make an alias:name, deprecate the old key:pool_id, and then slowly migrate it.Unfortunately, I didn’t understand what we were talking about. Can you please describe it in more detail?
You’re right! Thank you! It seems to me that there was a similar reason in one of the previous implementations because not in all cases, when working with monitoring, I had a State available. It looks like there are no more reasons, but I forgot to check this and remove the public storage for monitoring.
I’ll be waiting for your PR
These new processes are very lightweight and not linked to the caller process. In any case, we need to monitor the caller, and I don’t yet understand why a process that waits to see if the caller will crash is not suitable for this task.
It seems that this is excessive control over the execution logic on the call side. When using a pool, you should just perform some operation on it instead of juggling worker processes. What do you think?
I’m afraid that’s not right. The monitoring process is always killed after the worker is released.
Pay attention to this line: poolex/lib/poolex.ex at develop · general-CbIC/poolex · GitHub
Please tell me where I’m wrong if I don’t see something.
This is an excellent observation. Thank you very much! I focused on carefully handling errors from an already running worker and forgot about controlling their launch.
I didn’t understand
Why is this behavior strange, and how is it conceptually different from general supervisory behavior?
general-CbIC
I missed this question. As far as I know, Elixir does not have private modules, and there is no way to limit module accessibility. I added the
Privatespace to clarify to the developer that he is doing something wrong.Asd
First, the difference in application stop. When supervisor will stop Poolex process, this process will have some time to handle some messages and then terminate (if it has a
trap_exitflag) and only then the DynamicSupervisor will start terminating. Usually, Supervisors give some time for children to terminate and then, when some child is terminating for too long, Supervisor kills it. With spawning link to DynamicSupervisor directly, it would have no such timeout and the Supervisor of Poolex won’t even know that there’s some DynamicSupervisor which is terminating, therefore DynamicSupervisor won’t have time to terminate workers. If these workers were HTTP connections acceptors, they would just die, while they could terminate gracefully with controllable timeout.Second, the difference in fault tolerance. Right now if Poolex dies, all workers die with it. However, it is possible to just restart Poolex without restarting workers if dynamic supervisor and Poolex were started under the rest_for_one supervisor. Poolex would then just initialize it’s state and monitors from
which_childrenof DynamicSupervisor.And you can monitor it in the Poolex process without spawning extra one. This is what this checkout/checkin pattern is about: you store an association between a caller and a worker once worker is found and when caller or worker dies, you release the alive one.
I was talking about high priority for the dispatcher/manager process (Poolex process in your case) so that it checks out processes faster, since it manages the queue of work on it’s own and there’s no need to keep the messages in the messagebox. I thought that
poolboyuses it, but it does not, while a lot of other pools with similar architecture do (likehackney,lhttpc). But it would still make sense to use it inPoolexTrue, I am wrong, I missed this line completely.
That’s another user, not me.
But developer can use the
Private.DebugInfoandPrivate.Metricsmodules, right?And I also found one more bug. Consider this scenario:
Caller gets a worker, sends a long-running job to the worker, then caller dies and Poolex just returns this worker to the idle queue. In this case the next caller, can receive this worker which is still executing the long-running job and this caller won’t be able to execute anything with this worker. So I have a feeling that it might make sense to just restart the worker when caller dies before releasing this worker
sodapopcan
LOL, I miss @hst337.
The “official” way to mark a module private is to add
@moduledoc false, that way they don’t show up in the documentation (I can find a citation if needed). Looking through yourPrivatenamespace none of the moduledocs are very involved and could just be comments. Though I don’t think it’s a big deal either way, just pointing it out. Earmark uses a similar approach withEarmark.Internal.general-CbIC
If I understand correctly, you propose changing the circuit, as shown in the figure below. It seemed that if the Poolex process receives an
EXITmessage, it starts executing theterminate()callback after a short time. The first step in this callback is to turn off DynamicSupervisor: poolex/lib/poolex.ex at develop · general-CbIC/poolex · GitHub.On the Application Supervisor side, both trees are represented through a single input process: Poolex or Poolex Supervisor. I think that killing the input process will result in the same termination logic in both trees.
I completely agree with this. However, it is unclear where you can dump the state with information about which workers are busy and which are not.
general-CbIC
I need to think about it.
Oh, cool! I will check this out
Yep. And I can’t do anything with it. I can only say that it is for internal library usage. I love Elixir, but unfortunately, it doesn’t have a feature to hide library interfaces.
Thank you very much! I will add it to issues to not forget
However, I want to document private modules and functions for “future-me” or other Poolex contributors. Hexdocs is not all because developers still get access to each module’s public interfaces even if they aren’t documented.
ruslandoga
I learned recently that it might not be necessary to store the associations in the monitoring process since we can provide tags to :erlang.monitor which would be included in the DOWN message. And since the caller gives ref and resource back on checkin, we don’t need lookups there either.
Disclaimer: I didn’t read the whole thread or the Poolex code, but thought this little bit about monitors might be useful
Sorry if it’s completely irrelevant to the discussion!
ruslandoga
And maybe cleanups likeReading the docs a bit more carefully, it might not work. I’ll need to try out in a project tomorrow.GenServer.cast(pool_id, {:cancel_waiting, caller_reference})can be replaced with process aliases (available since OTP-24+, Elixir 1.15+) and the caller queue can maybe be replaced with just the message queue!Either way, it’d be really cool if Poolex used all the modern features of the Erlang VM! Then it would be another good reason to use it over Poolboy
general-CbIC
Hi, @ruslandoga!
Thanks a lot for the tip! At first glance, with the help of tags, we can refuse to store monitoring links to the “types of process” we monitor (
workerorwaiting caller). I’ll have to check it out.