Tideland EAS: agent concept
Yesterday I've introduced the new architecture of the Tideland EAS - formerly known as "Events and Services", now the "Erlang/OTP Application Server". One of the new concepts is the definition of agents for the control of business processes. Those agents are typical callback modules working like finite state machines. The state change is done by internal code, by the returning from services calls, or by the arguments of an external continuation after reaching a waypoint.
So here's one example of how to use the agent API from a view or a connector. First the agent will be started, then the reply will be handled asynchronously. Both, the agent and the reply call have a timeout. The first one defines the timespan in which the agent should complete its work, the second one is just for the reply call.
start_agent(Args) ->
% my_agent is the name of the callback module.
easagt:start(my_agent, Args, {5, hours}). process_agent(Aid) ->
case easagt:reply(Aid, {5, seconds}) of
{waypoint, foo, Info} ->
ContinuationArgs = handle_foo(Info),
easagt:continue(Aid, ContinuationArgs),
process_agent(Aid);
{waypoint, bar, Info} ->
ContinuationArgs = handle_bar(Info),
easagt:continue(Aid, ContinuationArgs),
process_agent(Aid);
{finished, error, Reason} ->
{error, Reason};
{finished, ok, Reply} ->
{ok, Reply};
{error, timeout} ->
{error, timeout}
end.
This is a very simplified code fragment, but it shows how a running agent can stop working when reaching a waypoint where external action - or interaction - is needed. The last shown error is the timeout of the reply call, the inner one can also be a timeout, in this case caused by the agent. If the timeouts aren't passed as integers representing milliseconds the time library of the CEL calculates them based on the tuples.
The control of state changes inside an agent is done through the result of the current processing.
process(foo, Args, StateData) ->
% Do something ...
{next, bar, NewArgs, NewStateData}. process(bar, Args, StateData) ->
% Do something else ...
{call, [{svc_a, req_a, ArgsA}, {svc_b, req_b, ArgsB}], baz, NewStateData}. process(baz, ServiceCallResults, StateData) ->
% Do something with the service call results ...
{waypoint, baz, Info, yadda, NewStateData}. process(yadda, ContinuationArgs, StateData) ->
% Do something with the continuation args ...
{stop, ok, Result}.
It's easy to see how one or more services can be called, the processing will continue when all services replied or after an optional timeout, or the control can be passed to an external waypoint. Additionally simple service calls can directly be done inside the callback functions.
{ok, Reply} = eassvc:call(svc_x, req_x, ArgsX, Timeout)
This API isn't stable yet, but it shows the direction. I'll write more about it here.


Comments [0]