README.md

Honeydew
========

Honeydew (["Honey, do!"](http://en.wiktionary.org/wiki/honey_do_list)) is a job queue + worker pool for Elixir.

- Workers are permanent and hold immutable state.
- Workers pull jobs from the work queue.
- Tasks are executed using `cast/2` and `call/2`, somewhat like a `GenServer`.
- If a worker crashes while processing a job, the job is recovered and placed back on the queue.

## Usage
Create a worker module and `use Honeydew`, then start the pool in your supervision tree with `Honeydew.child_spec/4`.

Honeydew will call your worker's `init/1` and will keep the state from `{:ok, state}`.

You can add tasks to the queue using `cast/2` or `call/2`, like so:

`Database.cast(:poolname, {:put, ["key", "value"]})`

`Database.call(:poolname, :up?)`

## Example

```elixir
defmodule Riak do
  use Honeydew

  @moduledoc """
    This is an example Worker to interface with Riak.
    You'll need to add the erlang riak driver to your mix.exs:
      `{:riakc, github: "basho/riak-erlang-client"}`
  """

  def init({ip, port}) do
    :riakc_pb_socket.start_link(ip, port)
  end

  def up?(riak) do
    :riakc_pb_socket.ping(riak) == :pong
  end

  def put(bucket, key, obj, content_type, riak) do
    :riakc_pb_socket.put(riak, :riakc_obj.new(bucket, key, obj, content_type))
  end

  def get(bucket, key, riak) do
    case :riakc_pb_socket.get(riak, bucket, key) do
      {:ok, obj} -> :riakc_obj.get_value(obj)
      {:error, :notfound} -> nil
      error -> error
    end
  end
end

```

Add the pool to your application's supervision tree:

```elixir
def start(_type, _args) do
  import Supervisor.Spec, warn: false

  children = [
    Honeydew.child_spec(:riak_pool, Riak, {'127.0.0.1', 8087})
  ]

  Supervisor.start_link(children, strategy: :one_for_one))
end
```

```elixir
iex(1)> Riak.call(:riak_pool, :up?)
true
iex(2)> Riak.cast(:riak_pool, {:put, ["bucket", "key", "value", "text/plain"]})
:ok
iex(3)> Riak.call(:riak_pool, {:get, ["bucket", "key"]})                       
"value"
```

## Worker State
Worker state is immutable, the only way to change it is to cause the worker to crash and let the supervisor restart it.

Your worker module's `init/1` function must return `{:ok, state}`. If anything else is returned or the function raises an error, the worker will die and restart after a given time interval (by default, five seconds).

## Pool Options

`Honeydew.child_spec/4`'s last argument is a keyword list of pool options.

See the [Honeydew](https://github.com/koudelka/honeydew/blob/master/lib/honeydew.ex) module for the possible options.


## Process Tree

```
Honeydew.Supervisor
├── Honeydew.WorkQueue
└── Honeydew.WorkerSupervisor
    └── Honeydew.Worker (x 10, by default)
```

## TODO:

- Failure backends
  - :abandon
  - :requeue
    - delay interval
    - action after max_tries (abandon, keep in queue, marshal)
- Distribution
  - jobs in mnesia, to allow other nodes to run work queues?

## Acknowledgements

Thanks to @marcelog, for his [failing worker restart strategy](http://inaka.net/blog/2012/11/29/every-day-erlang/).