gen_errand
is a behavior module to simplify interaction with unreliable external services.
The primary use case is fetching resources, eg a connection to a remote database which may be temporarily down or overloaded: while the first connection attempt may fail, it may succeed when retried at a later time. It is not limited to resource fetching tasks, however, in fact most use cases that need some form of retrying can be modeled, like posting a message to your favorite social network and retrying if it happens to be down.
gen_errand
relieves users of the burden of implementing retry and backoff logic over and over again, and to focus on the actual interaction logic instead.
Under the hood, gen_errand
is a simplified state machine, with only four possible states:
idle
: The errand is ready to start its a task.sleeping
: The errand is waiting in backoff before starting another attempt at doing its task.executing
: The errand is performing its task.done
: The errand has finished its task.
init(Args)
(Mandatory): Called to initialize the errand. Must return either anok
tuple with the initial data, the atomignore
, or astop
tuple with the stop reason.sleep_time(Attempt, Data)
(Mandatory): Called to determine the backoff time before starting the next attempt. Must return either anok
tuple with the backoff time and optionally updated data, orstop
or astop
tuple with the stop reason and optionally updated data.handle_execute(Data)
(Mandatory): Called when the errand starts performing its task. Must return an instruction or instruction tuple (see below).handle_event(EventType, Event, State, Data)
(Mandatory): Called when the errand receives a message. Must return an instruction or instruction tuple (see below).terminate(Reason, State, Data)
(Optional): Called when the errand is shutting down. The return value is ignored.code_change(OldVsn, State, Data, Extra)
(Optional): Called when the errand code is updated by a release upgrade. Must return anok
tuple with the new errand state and data.
The handle_execute/1
and handle_event/4
callbacks must return an instruction or instruction tuple to tell the errand how to proceed.
This instruction is only allowed when the errand is in the idle
state.
perform
: The errand will reset the attempt counter to0
, transition to thesleeping
state, wait for the time returned from the callback modules'sleep_time/2
function, then transition to theexecuting
state and start performing its task.{perform, NewData}
: The errand will update its data, reset the attempt counter to0
, transition to thesleeping
state, wait for the time returned from the callback modules'sleep_time/2
function, then transition to theexecuting
state and start performing its task.
idle
: The errand will transition to theidle
state.{idle, NewData}
: The errand will update its data and transition to theidle
state.
continue
: The errand will continue in the same state.{continue, NewData}
: The errand will update its data and continue in the same state.{continue, NewData, {Timeout, TimeoutMessage}}
: The errand will update its data and continue in the same state. If the givenTimeout
expires before another event occurs, an event of typetimeout
and the givenTimeoutMessage
is passed tohandle_event/4
.
stop
: The errand will terminate with reasonnormal
.{stop, Reason}
: The errand will terminate with the given reason.{stop, Reason, NewData}
: The errand will update its data and terminate with the given reason.
done
: The errand will transition to thedone
state.{done, NewData}
: The errand will update its data and transition to thedone
state.
repeat
: The errand will transition to theexecuting
state and retry immediately.{repeat, NewData}
: The errand will update its data, transition to theexecuting
state, and retry immediately.
retry
: The errand will increment the attempt counter, transition to thesleeping
state, and retry after the respective backoff time has elapsed.{retry, NewData}
: The errand will update its data, increment the attempt counter, transition to thesleeping
state, and retry after the respective backoff time has elapsed.
start/3,4
: Starts a standalone errand which is not linked to the calling process. Will return the errand pid in anok
tuple, or fail.start_link/3,4
: Starts an errand which is linked to the calling process. Will return the errand pid in anok
tuple, or fail.start_monitor/3,4
: Starts an errand which is monitored by the calling process. Will return the errand pid in anok
tuple, or fail.wait/2,3
: Wait for the errand to enter a given state within an optional timeout and returnok
, or fail when the timeout expires.call/2,3
: Send a synchronous call to the errand and wait for a reply within an optional timeout, or fail when the timeout expires.cast/2
: Send an asynchronous cast to the errand. Always returnsok
.reply/2
: Reply to a message received via a synchronous call message. Always returnsok
.stop/1,3
: Stops the errand with an optional reason and timeout. Will always returnok
, or fail when the timeout expires.
cooldown/5
: Calculates a backoff time from theAttempt
number, a constantDelay
, aBackoff
time which exponentially grows byGrowth
, and a randomJitter
.
See the examples
directory for examples how gen_errand
can be implemented and used.
- The
tcp_errand
example is simple, it tries to establish a TCP connection to a given host and port. - The
smtp_errand
example is more complex, as it not only involves connecting to a server but also some initial communication and possibly a socket upgrade to TLS.
- Maria Scott (Maria-12648430)
- Jan Uhlig (juhlig)