Honeydew
========
Honeydew (["Honey, do!"](http://en.wiktionary.org/wiki/honey_do_list)) is a job queue library for Elixir.
- Workers ("Honeys") are permanent and hold immutable state.
- Honeys pull jobs from the work queue ("Job List").
- Tasks are executed using `cast/1` and `call/2`, somewhat like a `GenServer`.
- If a Honey crashes while processing a job, the job is recovered and placed back on the queue.
## Usage
Simply create a module and `use Honeydew`, then start the pool with `YourModule.start_pool/2`.
You can request tasks be processed using `cast/1` or `call/2`, like so:
`Your.Module.cast({:insert_thing_in_db, ["key", "value"]})`
`Your.Module.call({:get_thing_from_db, ["key"]}) # -> "value"`
## Example
```elixir
# This is your worker
defmodule CatFeedingHoney do
use Honeydew
def init(pantry_location) do
Pantry.init(pantry_location)
end
# the last argument is always the honey's state
def make_snack(kind, kitty, pantry) do
snack = Pantry.get(pantry, :snacks, kind)
"#{snack} snack for #{kitty}!"
end
def go_shopping(pantry) do
Pantry.put(pantry, :snacks, :tuna)
IO.puts("went shopping")
end
end
# This is your database
defmodule Pantry do
def init(_location) do
{:ok, :pantry_connection}
end
def get(_pantry_connection, _shelf, item) do
item
end
def put(_pantry_connection, _shelf, item) do
item
end
end
CatFeedingHoney.start_pool("kitchen")
#
# Blocking call
#
CatFeedingHoney.call(:make_snack, [:tuna, :darwin]) # -> "tuna snack for darwin!"
#
# Non-blocking cast
#
CatFeedingHoney.cast(:go_shopping) # -> prints "went shopping"
#
# You can pass arbitrary functions to be executed, too.
#
CatFeedingHoney.call(fn(pantry) -> IO.inspect pantry end) # -> :pantry_connection
```
## Worker State
Worker State itself is immutable, the only way to change it is to cause the worker to crash and restart.
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).
## Configuration
### Worker Respawn Delay
You can configure the worker module's respawn delay time, in case `init/1` fails, like so:
`Your.Module.start_pool([:some_worker_args], init_retry_secs: 20)`
### Maximum Failures
Jobs are only allowed to fail a certain number of times before they are no longer retried (default, three times), this is also configurable via `start_pool/1`:
`Your.Module.start_pool([:some_worker_args], max_failures: 5)`
(at present, jobs that fail too much are abandoned, but in the future will be written to disk)
### Number of Workers
Honeydew will supervise the number of workers you specify (default, ten worker)
`Your.Module.start_pool[:some_worker_args], workers: 50)`
## Process Tree
After calling `Your.Module.start_pool/2`, a process tree like this will be created under the supervision of Honeydew:
```
Honeydew.Supervisor
└── Honeydew.Your.Module.HomeSupervisor
├── Honeydew.Your.Module.JobList
└── Honeydew.Your.Module.HoneySupervisor
├── Honeydew.Honey
├── Honeydew.Honey
├── Honeydew.Honey
└── Honeydew.Honey
```
## Disclaimer
This library is under active development, please don't use it in production yet, and please contribute if you find it useful! :)
## Acknowledgements
Thanks to @marcelog, for his [failing worker restart strategy](http://inaka.net/blog/2012/11/29/every-day-erlang/).