README.md

# Worker Pool [![Build Status](https://github.com/inaka/worker_pool/actions/workflows/erlang.yml/badge.svg)](https://github.com/inaka/worker_pool/actions/workflows/erlang.yml)[![codecov](https://codecov.io/gh/inaka/worker_pool/branch/main/graph/badge.svg)](https://codecov.io/gh/inaka/worker_pool)

<img src="https://img3.wikia.nocookie.net/__cb20140705120849/clubpenguin/images/thumb/f/ff/MINIONS.jpg/481px-MINIONS.jpg" align="right" style="float:right" height="400" />

A pool of gen servers.

## Abstract

The goal of **worker pool** is pretty straightforward: to provide a transparent way to manage a pool of workers and _do the best effort_ in balancing the load among them, distributing the tasks requested to the pool.

You can just replace your calls to the `gen_server` module with calls to the `wpool` module, i.e., wherever you had a `gen_server:call/N` now you put a `wpool:call/N`, and that’s it!

## Installation

Worker Pool is available on [Hex.pm](https://hex.pm/packages/worker_pool). To install, just add it to your dependencies in `rebar.config`:
```erlang
{deps, [{worker_pool, "~> 6.1"}]}.
```
or in `mix.ers`
```elixir
defp deps() do
  [{:worker_pool, "~> 6.1"}]
end
```

## Documentation

The documentation can be generated from code using [rebar3_ex_doc](https://github.com/starbelly/rebar3_ex_doc) with `rebar3 ex_doc`. It is also available online in [Hexdocs](https://hexdocs.pm/worker_pool/).

All user functions are exposed through the [wpool module](https://hexdocs.pm/worker_pool/wpool.html).

Detailed usage is also documented in the same [wpool module summary](https://hexdocs.pm/worker_pool/doc/wpool.html#content).

## Examples

Say your application needs a connection to a third-party service that is frequently used. You implement a `gen_server` called `my_server` that knows how to talk the third-party protocol and keeps the connection open, and your business logic uses this `my_server` as the API to interact with. But this server is not only a single-point-of-failure, but also a bottleneck.

Let's pool this server!

#### Starting the pool

First we need to start the pool, instead of starting a single server. If your server was part of your supervision tree, and your supervisor had a child-spec like:
```erlang
    ChildSpec = #{id => my_server_name,
      start => {my_server, start_link, Arguments},
      restart => permanent,
      shutdown => 5000,
      type => worker}.
```

You can now replace it by
```erlang
    WPoolOpts = [{worker, {my_server, Arguments}}],
    ChildSpec = wpool:child_spec(my_server_name, WPoolOpts),
```

#### Using the pool

Now that the pool is in place, wherever you've called the server, now you can simply call the pool: all code like the following
```erlang
    %% ...
    gen_server:call(my_server, Request),
    %% ...
    gen_server:cast(my_server, Notify),
    %% ...
```
can simply be replaced by
```erlang
    %% ...
    wpool:call(my_server, Request),
    %% ...
    wpool:cast(my_server, Notify),
    %% ...
```

If you want all the workers to get notified of an event (for example for consistency reasons), you can use:
```erlang
    wpool:broadcast(my_server, Request)
```

And if events have a partial ordering, that is, there is a subset of them were they should be processed in a strict ordering, for example requests by user `X` should be processed sequentially but how they interleave with other requests is irrelevant, you can use:
```erlang
    wpool:call(my_server, Request, {hash_worker, X})
```
and requests for `X` will always be sent to the same worker.

And just like that, all your requests are now pooled!

By passing a more complex configuration in the `WPoolOpts` parameter, you can tweak many things, for example the number of workers (`t:wpool:workers()`), options to pass to OTP's the `gen_server` engine behind your code `t:wpool:worker_opt()`, the strategy to supervise all the workers (`t:wpool:strategy()`), register callbacks you want to be triggered on worker's events (`t:wpool:callbacks()`), and many more. See `t:wpool:option()` for all options available.

#### Case studies used in production

To see how `wpool` is used you can check the [test](test) folder where you'll find many different scenarios exercised in the different suites.

If you want to see **worker_pool** in a _real life_ project, we recommend you to check [sumo_db](https://github.com/inaka/sumo_db), another open-source library from [Inaka](https://inaka.github.io/) that uses wpool intensively, or [MongooseIM](https://github.com/esl/MongooseIM), an Erlang Solutions' Messaging server that uses wpool in many different ways.

## Benchmarks

**wpool** comes with a very basic [benchmarker](https://github.com/inaka/worker_pool/blob/main/test/wpool_bench.erl) that let's you compare different strategies against the default `wpool_worker`. If you want to do the same in your project, you can use `wpool_bench` as a template and replace the worker and the tasks by your own ones.

## Contact Us

If you find any **bugs** or have a **problem** while using this library, please [open an issue](https://github.com/inaka/worker_pool/issues/new) in this repo (or a pull request :)).

## Requirements

**Required OTP version 25** or higher. We only provide guarantees that the system runs on `OTP25+` since that's what we're testing it in, but the `minimum_otp_vsn` is `"21"` because some systems where **worker_pool** is integrated do require it.