README.md

![directror travis test status](https://travis-ci.org/Pouriya-Jahanbakhsh/director.png?branch=master)

# Welcome

![directror](director.jpg)

**Director** is a production-ready **supervisor** and **manager** for Erlang/Elixir processes that focuses on speed, performance and flexibility.

## What is a supervisor?
According to the Erlang's manual:  
>A supervisor is a process that supervises other processes called child processes. A child process can either be another supervisor or a worker process. Supervisors are used to build a hierarchical process structure called a supervision tree, a nice way to structure a fault-tolerant application.  
>In Erlang we tell supervisors to start other processes. Every child process has its own options called childspec. 

## Features
* Don't worry about replacing **Director** with OTP/Supervisor Because a **Director** process is responsive for all API functions of OTP/Supervisor module. For example `supervisor:which_children(DirectorPid)` works.   
* **Director** has its own useful API functions too:  
	* `director:get_pid(DirectorRef, ChildId)` gives pid of child if child is alive.  
	* `director:get_pids(DirectorRef)` gives pids of alive children.  
	* `director:terminate_and_delete_child(DirectorRef, ChildId)` terminates and deletes a child in one request.  
	* `director:become_supervisor(DirectorRef, Pid, ChildSpec)` makes **Director** supervisor of alive process.  
	* `director:get_restart_count(DirectorRef, ChildId)` gives restart count of child (useful for debug). 
	* All functions not listed here.  
* **Director** is an Erlang behaviour and every callback-module of this behaviour should has following callback-functions (See "How to use?" section for detailed explanation):  
	* `init/1`: For initialization. Here you can define children database type, debug mode and childspecs of some children that you want to start them in initialize state.  
	* `handle_start/4`: Will be called after starting a child process.  
	* `handle_exit/5`: Will be called after crashing a child process. This callback-function should tell **Director** how to deal with process crash. Restart child? Restart it after time interval? Delete child from children? Do nothing? Terminate yourself?  
	* `handle_terminate/5`: Will be called when **Director** terminates a child process.  
	* `terminate/2`: Will be called for termination of **Director** itself.  
* Use different databases for keeping children. By default **Director** uses an Erlang list. It has two other modes for keeping children In an ETS or Mnesia table.  
* A number of **Director** processes can use one ETS table on same node (sharing ETS table).
* A number of **Director** processes can use one Mnesia table on cluster of nodes (sharing Mnesia table).
* By sharing table **Director**s can start, restart, terminate, etc a number of children simultaneously.  
* By using ETS or Mnesia you can read children info directly from table using API functions of `director_table_ets` and `director_table_mnesia` modules instead of getting them from **Director** process. For example `director_table_ets:get_pids(Tab)` or `director_table_mnesia:count_children(Tab)`.  
* You can define your own database for keeping children by implementing `director_table` behaviour. Also some test cases are ready for testing your module.  
* Understandable debug output for every operation.  

All features not listed here. For more info see Guide and examples. For contributing see [`CONTRIBUTING.md`](CONTRIBUTING.md) file.


## How to use?
 Since **Director** is an Erlang behaviour; So before explaining its workflow, I'll explain that "What a behaviour is?" for newbies.  
> In Erlang, a behaviour is a design pattern implemented in a module/library. It provides functionality in a fashion similar to inheritance in object-oriented programming. A number of functions called callback-functions must be defined for each behavior to work in a module called callback-module.  

When you want to start a **Director** process, you should specify your callback-module which has **Director**'s callback-functions defined and exported. In run-time, **Director** process calls those callback-functions in different states.  I'll explain those callback-functions in below.  
### `init/1`
For starting a linked **Director** process, you should call one of the API functions:
```erlang
director:start_link(Module::module(), InitArg::any()).
director:start_link(Module::module(), InitArg::any(), Opts::director:start_options()).
director:start_link(RegisterName::director:register_name(), Module::module(), InitArg::any()).
director:start_link(RegisterName::director:register_name(), Module::module(), InitArg::any(), Opts::director:start_options()).
```
For starting an stand-alone **Director** process, you should call one of the API functions:
```erlang
director:start(Module::module(), InitArg::any()).
director:start(Module::module(), InitArg::any(), Opts::director:start_options()).
director:start(RegisterName::director:register_name(), Module::module(), InitArg::any()).
director:start(RegisterName::director:register_name(), Module::module(), InitArg::any(), Opts::director:start_options()).
```
After calling, **Director** process calls `Module:init(InitArg)`, possible return values of `init/1` are:
```erlang
-type init_return() :: 'ok' % will be {ok, undefined, [], director:default_childspec(), []}
                     | {'ok', director:state()}
                     | {'ok', director:state(), [director:childspec()]|[]}
                     | {'ok', director:state(), [director:childspec()]|[], director:default_childspec()}
                     | {'ok', director:state(), [director:childspec()]|[], director:start_options()}
                     | {'ok', director:state(), [director:childspec()]|[], director:default_childspec(), director:start_options()}
                     | 'ignore'
                     | {'stop', Reason::any()}.
```
#### Childspec
A childspec is an Erlang map containing some mandatory and optional keys that belongs to one child process. I will explain these keys below.  
* **id:** This key should be unique for child and can be any erlang term. We will understand usage of this key later. This key is mandatory.  
* **start:** This key should be an `mfa()` (module, function and its arguments) and is mandatory. **Director** calls `erlang:apply(Mod, Func, Args)` using value of this key for starting child. Possible values of this key are:  
	* `mfa()`.  
	* `{module(), Func::atom()}`. Will be `{module(), Func::atom(), []}`.  
	* `module()`. Will be `{module(), start_link, []}`.  
* **state:** This key is optional and if not defined, its default value will be atom `undefined`. This is state data of child process inside **Director** process. We will understand usage of this key later.  
* **type:** A child process can be a worker or another supervisor. This key is optional and default value is atom `worker` and possible values are:  
	* `supervisor`.  
	* `sup` (Short for `supervisor`). 
	* `worker`.  
	* `w` (Short for `worker`).  
	* `s` (Short for `supervisor`).  
* **terminate_timeout:** When one **Director** process is terminating or when you call `director:terminate_child/2` or `director:terminate_and_delete_child/2` for a child,  It terminates its alive children or that child using `erlang:exit(ChildPid, Reason)`. Possible values are atom `infinity` and `0` and positive integers. When value is `0`, **Director** calls `erlang:exit(ChildPid, kill)` otherwise when this value is `X`, it calls `erlang:exit(ChildPid, shutdown)` and waits `X` milli-second for termination. If child did not terminate after `X`, It calls `erlang:exit(ChildPid, kill)`. This key is optional and if key `type` defined to `worker` its default value will be `5000` otherwise `infinity`. 
* **delete:** When **Director** process is in termination state, It deletes all children from its database (from list, ETS, Mnesia, etc). When you have some **Director** processes and they are using one ETS, Mnesia, etc table for keeping their children (Using shared table), If one **Director** stops, You can restart its children from another **Director** which has access to that table for every child that has this key `=> false` in its childspec. Default is `true`.   
* **modules:** This key is optional and its default value will be 1st element of its start `mfa`. Possible values are:  
	* `[module()]`.
	* `dynamic` (Where name of callback module will determine in future. for example in `gen_event` process).  
* **append:** What is usage of `director:default_childspec()` in above? If you want to have a number of children with similar childspec options, you can define a default childspec in return value of `init/1` and all children with `append => true` in their childspec will combine with that default childspec. Default childspec is like normal childspec except that this has not `id` and `append` keys. Default default childspec is:
	```erlang
	#{terminate_timeout => 0, modules => [], type => worker, delete => true, state => undefined}
	```  

#### Start options
You can define start options in two places, in calling `director:start/2-4` or `director:start_link/2-4` and in return value of `init/1`. Start options is an Erlang proplist with following items:
* **db:** Should be another erlang proplist with following items:
	* **table:** Default is `list`. Can be one of `list`, `ets` or `mnesia`. If you defined new table `x`,name your module `director_table_x` and define value of `table` to `x`.  
	* **init_arg:** If you are using `list` for keeping children, Its value not matters. for `mnesia` and `ets` must be table name. For new database callback module if this value not defined, atom `undefined` will be given to callback-function `director_table_x:create/1` and if defined, argument `{value, Value}` will be given.  
	* **delete:** Should be one of `true` or `false`. If defined to `true`. In termination of **Director** itself, It deletes the table. Default is `true`.  
* **debug:** Standard OTP/sys debug options:  
	```erlang
	-type dbg_opts() :: [dbg_opt()] | [].
	-type  dbg_opt() :: {'trace', 'true'}
                      | {'log',
                         {N :: non_neg_integer(),
                          [{Event :: system_event(),
                            FuncState :: _,
                            FormFunc :: format_fun()}]}}
                      | {'statistics', {file:date_time(),
                                        {'reductions', non_neg_integer()},
                                        MessagesIn :: non_neg_integer(),
                                        MessagesOut :: non_neg_integer()}}
                      | {'log_to_file', file:io_device()}
                      | {Func :: dbg_fun(), FuncState :: term()}.
	```  
	Default is nothing or `[]`. For more info see OTP `sys` module.  
* **spawn_opt:**: List of spawn options. For more info see OTP type `proc_lib:spawn_option()`. You cannot use this option in return value of `init/1`.  
* **timeout:** How much time process needs for initialization? You cannot use this option in return value of `init/1`.  

#### `init/1` examples
```erlang
-module(director_test).
...
-export([init/1]). % Do not forget to export it
...
-record(state, {}). %% State record for Director itself
-record(chstate, {}). %% State record for Director children

init(MyInitArg) ->
	Child_1 = #{id => child_1
	           ,start => {child_1_module, start_link, [MyInitArg]}
	           ,state => #chstate{}
	           ,terminate_timeout => 1000},
	Child_2 = #{id => child_2
	           ,start => {child_2_module, start_link, [MyInitArg, arg_2, arg_3]}
	           ,state => #chstate{}},
	{ok, #state{}, [Child_1, Child2], [{db, [{table, ets}, {init_arg, my_table}]}]}.
	%% In above if you want some children with similar childspecs, you can use default childspec:
%	Children = [#{id => Id, append => true} || _ <- lists:seq(1, 100)],
%	DefChildSpec = #{start => {foo, start, [arg_1, arg_2]}},
%	{ok, #state{}, Children, DefChildSpec}.
	%% You will have 100 childspecs with ids 1-100 and start {foo, start, [arg_1, arg_2]}
	
	%% If you want simple_one_for_one strategy of OTP/Supervisor:
%	Children = [#{id => Id, append => true, start => {foo, start, [arg_3]}} || _ <- lists:seq(1, 100)],
%	DefChildSpec = #{start => {foo, start, [arg_1, arg_2]}},
%	{ok, #state{}, Children, DefChildSpec}.
	%% You will have 100 childspecs with ids 1-100 and start {foo, start, [arg_1, arg_2, arg_3]}
...
```

### `handle_start/4`
When **Director** starts a child process, It calls:  
```erlang
YourCallbackModule:handle_start(ChildId, ChildState, DirectorState, Metadata)
```
In above example when **Director** starts `Child_1`, It calls:  
```erlang
director_test:handle_start(child_1, #chstate{}, #state{}, #{restart_count := 0, pid := PidOfChild_1})
```
This callback-function should yield:
```erlang
-type handle_start_return() :: {'ok', NewChildState::director:child_state(), NewDirectorState::director:state(), director:callback_return_options()}
                             | {'stop', director:child_state(), Reason::any(), director:callback_return_options()}.
```
Example of `handle_start/4` which just tells **Director** to don't call `error_logger` about starting any child:
```erlang
handle_start(_, ChState, State, _) ->
	{ok, ChState, State, [{log, false}]}.
```


### `handle_exit/5`
When a child process crashes, Its **Director** will receive child's exit signal and calls:  
```erlang
YourCallbackModule:handle_exit(ChildId, ChildState, ReasonOfChildTermination, DirectorState, MetaData)
```
In above example when `Child_1` exits with reason `oops`, **Director** calls:  
```erlang
director_test:handle_exit(child_1, #chstate{}, oops, #state{}, #{restart_count := 1})
```
This callback-function should yield:
```erlang
-type handle_exit_return() :: {'ok', director:child_state(), director:state(), director:action(), director:callback_return_options()}
                            | {'ok', director:child_state(), director:state(), director:callback_return_options()}. %% will be {'ok', director:child_state(), director:state(), restart, director:callback_return_options()}
-type  action() :: 'restart'
                 | {'restart', pos_integer()}
                 | 'delete'
                 | 'wait'
                 | 'stop'
                 | {'stop', Reason::any()}.
```
Example of `handle_exit/5` which tells **Director** to restart child after 1000 milli-seconds:
```erlang
handle_exit(_, ChState, _, State, _) ->
	{ok, ChState, State, {restart, 1000}, []}.
```
If you define `delete` as action, Child will be removed from children table. If you define `wait` as action, **Director** does nothing and you have to call `director:restart_child(DirectorProc, ChildId)` for restarting child. If you define `stop`, **Director** will terminate itself with error reason of child crash.  
What if you define `restart` or `{restart, Int}` and child does not restart?  **Director** will restart child again, So calls `handle_exit/5` again which its metadata argument has `restart_count` key plus one. For example in following code **Director** will restart child id `foo` for 5 times, then restarts it after 1000 milli-seconds for 6th time, and finally terminates itself with reason `{max_restart, foo}` for 7th time:  
```erlang
handle_exit(foo, ChState, _Reason, State, #{restart_count := RC}) when RC < 6 ->
	{ok, ChState, State, restart, []};
handle_exit(foo, ChState, _Reason, State, #{restart_count := 6}) ->
	{ok, ChState, State, {restart, 1000}, []};
handle_exit(foo, ChState, _Reason, State, _) ->
	{ok, ChState, State, {stop, {max_restart, foo}}, []}.
```

### `handle_terminate/5`
When you call `director:terminate_child/2-3` or `director:delete_and_terminate_child/2-3`, **Director** terminates child process and calls:  
```erlang
YourCallbackModule:handle_terminate(ChildId, ChildState, ReasonOfChildTermination::shutdown|kill|term(), DirectorState, MetaData)
```
Also it calls `handle_terminate/5` when it is in terminate state and is terminating its alive children.  
For above example when you call `director:terminate_child(DirectorProc, child_2)`, It calls:  
```erlang
director_test:handle_exit(child_2, shutdown, #chstate{}, #state{}, #{restart_count := 0})
```
This callback-function should yield:  
```erlang
-type handle_terminate_return() :: {'ok', director:child_state(), director:state(), director:callback_return_options()}.
```
For example following code tells **Director** don't call `error_logger` for termination of child id `bar` and call it for other children:  
```erlang
handle_terminate(bar, ChildState, _, DirectorState, _) ->
	{ok, ChildState, DirectorState, [{log, false}]};
handle_terminate(_, ChildState, _, DirectorState, _) ->
	{ok, ChildState, DirectorState, []}. % Default log value is true
```

### `terminate/2`
When director is terminating itself, after terminating its alive children, It calls:  
```erlang
YourCallbackModule:terminate(ReasonOfTermination, DirectorState)
```
For above example if **Director** is terminating with reason `normal`, it calls:  
```erlang
director_test:terminate(normal, #state{})
```
This callback-function should yield:  
```erlang
-type terminate_return() :: {'ok', callback_return_options()}
                          | {'new_error', NewReason::any(), callback_return_options()}
                          | any().
```
For example following code tells **Director** to change crash reason `oops` to `normal` and do not call `error_logger` about terminating yourself:
```erlang
terminate(oops, State) ->
	{new_error, normal, [{log, false}]}.
```
Anything other than {`ok`, Opts}` and `{new_error, _, Opts}` causes **Director** to call `error_logger` and exit with its crash reason.  

# Build
## Compile
```sh
~/director $ make
===> Verifying dependencies...
===> Compiling director
```

## Use as dependency
##### Rebar3
Put this in deps in rebar.config:
```erlang
{director, "18.2.20"}
```

##### Rebar
Put this in deps in rebar.config:
```erlang
{director, ".*", {git, "https://github.com/Pouriya-Jahanbakhsh/director.git", {tag, "18.2.20"}}}
```

##### Mix
Put this in deps in mix.exs:
```elixir
{:director, "~> 18.2.20"}
```

##### erlang.mk
```make
dep_director = hex 18.2.20
```

#### API documentation
```sh

 /projects/director $ make doc
===> Verifying dependencies...
===> Fetching edown ({pkg,"edown","0.8.1"})
===> Compiling edown
===> Compiling director
===> Running edoc for director

 /projects/director $ ls doc/ | grep .md
director.md
director_table_ets.md
director_table_mnesia.md
README.md
```

# Todo
* Add test for having something like OTP/Supervisor `simple_one_for_one` strategy.  
* Add complete examples.  
* Add documentation for writing new `director_table` behaviour based module.  


### License
**`BSD 3-Clause`**


### Author
**`pouriya.jahanbakhsh@gmail.com`**


### Hex
[**`18.2.20`**](https://hex.pm/packages/director)