README.md

# SpaghettiPool
A worker pool library that helps you benefit from ETS table concurrency.

ETS tables allow both read and write concurrency, but this comes with zero
safety guarantees when writing to a single key concurrently. `SpaghettiPool`
is built using `Erlang`'s `:gen_fsm`-behaviour, and distinguishes between read
and write requests. Only a single write worker per key can be checked out at
any time, this restriction does not apply to read workers.

Requests are handled in batches; while handling writes only write workers will
be checked out, and vice versa while handling reads.

It is also possible to lock and unlock the pool, in order block all access to
the table while performing maintenance tasks.

## Documentation



## Installation

(Note: this is package is currently under active development and not released
on Hex yet. Step 2 does apply.)

This package is [available in Hex](https://hex.pm/packages/spaghetti_pool), the package can be installed as:

  1. Add `spaghetti_pool` to your list of dependencies in `mix.exs`:

    ```elixir
    def deps do
      [{:spaghetti_pool, "~> 0.0.1"}]
    end
    ```

  2. Ensure `spaghetti_pool` is started before your application:

    ```elixir
    def application do
      [applications: [:spaghetti_pool]]
    end
    ```

## Usage

Below is a minimal example of how you can start using `SpaghettiPool` in your
application:

```elixir
defmodule MyApp.Mixfile do
  use Mix.Project

  def project do
    [app: :my_app,
     version: "0.0.1",
     elixir: "~> 1.3-dev",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps]
  end

  def application do
    [applications: [:logger, :spaghetti_pool],
     mod: {MyApp, []}]
  end

  defp deps do
    [{:spaghetti_pool, git: "https://github.com/theemuts/spaghetti_pool.git", branch: "master"}]
  end
end
```

```elixir
defmodule MyApp do
  @moduledoc false
  use Application

  @doc false
  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    {:ok, _} = Application.ensure_all_started(:spaghetti_pool)
    pool_opts = [name: {:local, MyApp.Pool}, worker_module: MyApp.Pool.Worker]
    worker_args = :ok

    children = [SpaghettiPool.child_spec(MyApp.Pool, pool_opts, worker_args)]

    Supervisor.start_link(children, [strategy: :one_for_one, name: MyApp.PoolSupervisor])
  end

  @doc false
  def read_something() do
    worker = SpaghettiPool.checkout(MyApp.Pool, :read)
    result = MyApp.Pool.Worker.do_something(worker)
    SpaghettiPool.checkin(MyApp.Pool, worker, :read)

    result
  end

  @doc false
  def write_something(key) do
    worker = SpaghettiPool.checkout(MyApp.Pool, {:write, key})
    result = MyApp.Pool.Worker.do_something(worker)
    SpaghettiPool.checkin(MyApp.Pool, worker, {:write, key})

    result
  end

  @doc false
  def read_something_transaction() do
    SpaghettiPool.transaction(MyApp.Pool, :read, &MyApp.Pool.Worker.do_something/1)
  end
end
```

```elixir
defmodule MyApp.Pool.Worker do
  @moduledoc false

  use GenServer

  @doc false
  def start_link(worker_args) do
    GenServer.start_link(__MODULE__, worker_args)
  end

  @doc false
  def init(_), do: {:ok, nil}

  @doc false
  def do_something(worker) do
    GenServer.call(worker, :do_something)
  end

  @doc false
  def handle_call(:do_something, _from, state) do
    {:reply, :something, state}
  end
end
```

By default, this pool will have 10 workers, a maximum overflow of 10 workers,
and these workers are assigned in a last-in-first-out manner. See the
documentation for more information and options.