Fl4m3Ph03n1x

Fl4m3Ph03n1x

Why should I uses Tasks instead of Pools?

Background

A book I am reading makes the following statement:

Tasks will allow us to do one-off jobs and still rely on the rich OTP library. Connection pooling libraries let us share long-running connections across processes.

So, my understanding of this is as follows:

  • if I have a one off task, I should use tasks
  • if I need persistent connections to something, I should use pools

Why not always use pools?

And this is my questions. Even if I have one-off tasks, I still think pools are superior because they don’t have to create a new process to do the work, everything is already created.

I could understand the argument here if using pools was significantly harder than using one-off Tasks, but today with libraries like poolboy and books like Elixir in Action I do feel it is easy to understand, create and use pools these days.

Questions

  • What are the major benefits of using one-off Tasks instead of a Pool?
  • In what scenario would I be better of using a one-off Task instead of a Pool?

Marked As Solved

peerreynders

peerreynders

For me the whole point of the BEAM is that processes are lightweight and creating processes is inexpensive.

This property makes it very attractive to view processes a disposable - much in the same way as memory is “disposable” in a garbage collected language.

This is also helpful when it comes to resilience. If a process is disposed of after a single use, it is much less likely that you’ll end up with a situation where some weird edge case is corrupting your process state over time.

If the process lifetime is short enough, memory may never have to be garbage collected and is simply returned wholesale to the system after process termination.

So, personally I would favour short lived processes over long lived ones - until there is a good reason for making it long lived.

if I need persistent connections to something, I should use pools

More generally, if your process works with resources that are expensive to create and reusable then it should be a worker in a pool. Connection pools are built around that concept because database connections are expensive to create and there is an effective, finite limit to the quantity of simultaneous database connections.

The typical tradeoff is that long-lived processes should be as simple as possible to minimize the danger of state corruption over the long term. So it wouldn’t be that unusual for a long-lived process to delegate a lot of its work to short-lived processes while the long-lived process only manages simple state transitions.

Furthermore, from the resilience perspective, it may be worthwhile making worker processes “perishable” - terminate them once they have been used too often or aged too much and replace them with a fresh worker (and resources).

Also Liked

NobbZ

NobbZ

The only 2 reason I could spontaniously come up are those:

  1. Pools also always introduce some kind of rate limit or upper bound for concurrent processing, while you can spawn as many tasks as you want. Of course there are pools which can handle this by dynamically spawn processes when exhausted, but if this occurs often, then you are basically back to even slower spawning tasks.
  2. Task is part of the standard library, pools aren’t.
lud

lud

Well for some reasons people could use the process dictionary, for random numbers related stuff for instance. Or some people could register the process to a registry, use a library that would link/monitor the process from another process, create ETS tables that would not be destroyed.

hauleth

hauleth

Because often we pool other resources as well. Sometimes these resources are limited (RAM when you do image processing, you want to limit amount of the images processed at once to keep RAM usage in reasonable boundaries), sometimes setup can be long and it is better to do it once and then keep it alive for longer time (HTTP keep-alive connections), and sometimes there is mix of both of those (for example DB connections).

hauleth

hauleth

You can do, but this will be applied globally (by reducing amount of schedulers). Alternatively you could use :max_children option in Task.Supervisor or DynamicSupervisor, but that will have problem as there is no queuing mechanism and it will simply return error when trying to start new job. So it all depends on your use case, in the end we have DynamicSupervisor which in most cases will be more than enough, and pools are similar to that (when :max_children applied), but with additional features (like queuing and ending tasks that take too long).

lud

lud

Also pooled processes must have a cleanup step if they will perform many, independant, unrelated tasks.

A dedicated Task process will have everything cleanup up upon completion.

Where Next?

Popular in Questions Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
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
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
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
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
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
Exadra37
Sometimes I want to check if the input into a function is not a blank string. My first approach: defmodule Example do def do_stuff(s...
New
Mooodi
Given a string, how can I get access to its character by index? Enum.at("my_string", 2) doesn't work. Or rather, not char, but a substr...
New

Other popular topics Top

yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
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
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
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
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
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
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
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

We're in Beta

About us Mission Statement