# ExPool
A generic pooling library for Elixir.
[Documentation for ExPool is available online](http://hexdocs.pm/ex_pool/).
## Installation
Add ExPool to your list of dependencies in `mix.exs`:
```elixir
def deps do
[{:ex_pool, "~> 0.1.1"}]
end
```
## Usage
ExPool uses a set of initialized processes kept ready to use rather than spawning and destroying them on demand.
When you run a function on the pool:
1. It requests a process from the pool.
2. Runs the function with the pid as only argument.
3. Returns the process to the pool.
If there are no processes available it blocks until a process is returned to the pool, and then runs the function.
### The worker
The worker is a module that fits into a supervision tree (for example, a GenServer).
It is the process the pool will initialize and keep ready to use.
The following snippet shows an example of a worker that uses `:timer.sleep\1` to simulate a long-lasting operation (like a CPU intensive task, an external http request or a database query).
```elixir
defmodule HardWorker do
use GenServer
def start_link(_opts \\ []) do
GenServer.start_link(__MODULE__, :ok, [])
end
def do_work(pid, milliseconds \\ 2000) do
GenServer.call(pid, milliseconds)
end
def handle_call(milliseconds, _from, state) do
:timer.sleep(milliseconds)
IO.puts "Work done!"
{:reply, :ok, state}
end
end
```
Is recommended to always use blocking calls when using a worker (i.e. `GenServer.call/2` instead of `GenServer.cast/2`).
If a non-blocking call is used ExPool will return the process to the pool and make it available for other requests even though it may be performing work.
### The pool
You can start a pool with `ExPool.start_link/1`.
In the following example we create a pool and run a function on it.
```elixir
{:ok, pool} = ExPool.start_link(worker_mod: HardWorker)
ExPool.run pool, fn (worker) ->
HardWorker.do_work(worker, 1000)
end
# It will print:
# Work done!
```
We have created a pool that spawns a set of workers (5 by default) and we have run a function on the pool.
If we run concurrently more functions on the pool than workers available the functions that overflow the number of workers will block until there is a worker available.
```elixir
{:ok, pool} = ExPool.start_link(worker_mod: HardWorker, size: 2)
for _i <- 1..5 do
spawn_link fn ->
ExPool.run pool, &HardWorker.do_work(&1)
end
end
# It will print:
# Work done!
# Work done!
# Work done!
# Work done!
# Work done!
```
### Using a pool on a supervision tree
To start a pool that will be supervised by your application add to your application supervisor (or any other supervisor of your choice) the following child.
```elixir
defmodule MyApplication do
use Application
def start(_type, _args) do
import Supervisor.Spec, warn: false
children = [
worker(ExPool, [[worker_mod: HardWorker, size: 10, name: :my_pool]])
# ... more children
]
opts = [strategy: :one_for_one, name: Transactions.Endpoint.Supervisor]
Supervisor.start_link(children, opts)
end
end
```
The pool will be started with your application and can be used as follows.
```elixir
ExPool.run :my_pool, fn (worker) ->
HardWorker.do_work(worker)
end
```
## Examples
### A pool of redis connections
The following example will show how to use ExPool to keep a pool of redis connections on your application. We will use ExRedis to establish a redis connection and run commands.
First we add ExPool and ExRedis as dependencies of the application in our `mix.exs`.
```elixir
defp deps do
[{:ex_pool, "~> 0.1.1"},
{:exredis, ">= 0.2.2"}]
end
```
Run `mix deps.get` to get the dependencies from Hex.
Add to our `config/config.exs` some configuration about the pool and the redis server.
```elixir
config :redis_pool,
worker_mod: ExRedis,
size: 10,
name: :redis
config :ex_redis,
host: "127.0.0.1",
port: 6379,
password: "",
db: 0
```
As ExRedis fits into a supervision tree there is no need to explicitly define a worker.
We have configured a pool with 10 redis connections named `:redis`. To start the pool when the application starts add it as a child of your application supervisor.
```elixir
defmodule MyApplication do
use Application
@redis_pool_config Application.get_env(:redis_pool)
def start(_type, _args) do
import Supervisor.Spec, warn: false
children = [
worker(ExPool, [@redis_pool_config])
]
opts = [strategy: :one_for_one, name: Transactions.Endpoint.Supervisor]
Supervisor.start_link(children, opts)
end
end
```
Now you can run commands on redis from your application.
```elixir
ExPool.run :redis, fn (client) ->
client |> Exredis.query ["SET", "foo", "bar"]
end
ExPool.run :redis, fn (client) ->
client |> Exredis.query ["GET", "foo"]
end
# => "bar"
```
## License
ExPool source code is released under Apache 2 License.
Check LICENSE file for more information.