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 anoktuple with the initial data, the atomignore, or astoptuple with the stop reason.sleep_time(Attempt, Data)(Mandatory): Called to determine the backoff time before starting the next attempt. Must return either anoktuple with the backoff time and optionally updated data, orstopor astoptuple 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 anoktuple 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 thesleepingstate, wait for the time returned from the callback modules'sleep_time/2function, then transition to theexecutingstate and start performing its task.{perform, NewData}: The errand will update its data, reset the attempt counter to0, transition to thesleepingstate, wait for the time returned from the callback modules'sleep_time/2function, then transition to theexecutingstate and start performing its task.
idle: The errand will transition to theidlestate.{idle, NewData}: The errand will update its data and transition to theidlestate.
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 givenTimeoutexpires before another event occurs, an event of typetimeoutand the givenTimeoutMessageis 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 thedonestate.{done, NewData}: The errand will update its data and transition to thedonestate.
repeat: The errand will transition to theexecutingstate and retry immediately.{repeat, NewData}: The errand will update its data, transition to theexecutingstate, and retry immediately.
retry: The errand will increment the attempt counter, transition to thesleepingstate, and retry after the respective backoff time has elapsed.{retry, NewData}: The errand will update its data, increment the attempt counter, transition to thesleepingstate, 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 anoktuple, or fail.start_link/3,4: Starts an errand which is linked to the calling process. Will return the errand pid in anoktuple, or fail.start_monitor/3,4: Starts an errand which is monitored by the calling process. Will return the errand pid in anoktuple, 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 theAttemptnumber, a constantDelay, aBackofftime which exponentially grows byGrowth, and a randomJitter.
See the examples directory for examples how gen_errand can be implemented and used.
- The
tcp_errandexample is simple, it tries to establish a TCP connection to a given host and port. - The
smtp_errandexample 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)