Akka finite state machine instances

JavaScalaAkkaState Machine

Java Problem Overview


I am trying to leverage Akka's finite state machine framework for my use case. I am working on a system that processes a request that goes through various states.

The request here is the application name that needs to be deployed along with the application it depends on:

Request for application A -> A is in a QUEUED state
Discover A's dependency B -> B is in a QUEUED state
B is being processed -> B is in a PROCESSING STATE
A is being processed -> A is in a PROCESSING STATE
B is processed -> B is in a DONE state
A is processed -> A is in a DONE state

For this I am initializing a finite state machine at discovery time. So A's FSM is created when the request comes in, B's FSM is initialized when B is discovered from one of the actors.

Do I initialize and pass the FSM instance to all the actors, and at the same time tell the FSM about the operations being performed on the data so that the state machine goes in the correct state?

Here is the relevant portion of the state machine:

when(QUEUED, matchEvent(requestAccepted.class, MyApp.class,
    (requestAccepted, service) -> 
    goTo(PROCESSING).replying(PROCESSING)));

when(PROCESSING, matchEvent(completed.class, MyApp.class,
    (completed, service) -> goTo(DONE).replying(DONE)));

// During transitions, save states in the database.
onTransition(matchState(PROCESSING, DONE, () -> {
  nextStateData().setServiceStatus(DONE);
  databaseWriter.tell(nextStateData(), getSelf());

And here is an example from one of the actors processing the request:

ProcessingActor extends AbstractActor {

    @Override
      public void onReceive(Object message) throws Throwable {
        if (message instanceof processApplication) {
         // process the app
         // Initialize FSM for the Application
         FSM myFSM = Props.create(MYFSM.class);
         myFSM.tell( new completed(processApplication.app)
    }

Is this the right way to initialize the state machine and use it? Or should the initialization happen in the constructor of the ProcessingActor? But in that case there wouldn't be one state machine per application (data).

Java Solutions


Solution 1 - Java

TL;DR; While the description of states and transitions is rather ambiguous in the OP, the status of the chosen implementation, its implications and those of alternatives can be addressed.

The implementation at hand can count as an "actor per request" approach, as opposed to what more frequently can be found, where the state-machine actor buffers and handles multiple requests. It will be presented further below.

In its present form the given implementation is the only AbstractFSM per request I know of, actually with yet another FSM downstream, which then can't be called "per request" any more and likely could be avoided. It looks roughly like this:

actor per request FSM

The "actor per request" initially seems to have come up in this discussion, which gave raise to this blog and a bit later even occasionally claimed the title of a pattern. It seems in part it was motivated by replicating one of the features of the Spray framework, sort of precursor of Akka http.

Another discussion on SO came to an inconclusive result on the question whether to prefer a single actor over one per request, and presents routing as a 3rd alternative, because a router also acts as a load balancer, touching the back-pressure topic.

The approach more frequently presented in combination with AbstractFSM and its varieties, though, is a single FSM actor that buffers incoming messages utilizing the whenUnhandled method to accumulate them by default, no matter what the current state is (e.g. Akka in Action, chapter "Finite State Machines and Agents" or the Lightbend buncher example). I'm not able to back up the claim with a reference, but it looks like AbstractFSM is more thought of to model the states of an actor that deals with multiple requests, rather than that of a request that goes through multiple stages. Related to the OP that approach would mean, that ProcessingActor itself could extend AbstractFSM.

public class ProcessingActor extends AbstractFSM<sample.so.State, RequestQueue> {
{
    startWith(Idle, new RequestQueue());

    when(Idle, /* matchEvent(EventType, DataType, Action) */
            matchEvent(
                    Request.class,
                    RequestQueue.class,
                    (request, queue) -> goTo(Processing).using(queue.push(request)));
    /* more state-matchers */

    whenUnhandled(
            matchEvent(
                    Request.class,
                    RequestQueue.class,
                    (request, qeue) -> stay().using(qeue.push(request)));

    initialize();
}

Arriving at something like this for the "request"-part, where the database writes aren't represented by states any more, rather than state entry and exit actions. Note that all the whenUnhandled branch doesn't appear in the diagram, since it isn't related to a state change.

single actor FSM

Without going too much into weighing (vague) requirements against the chosen implementation, AbstractFSM seems to be a rather clumsy machinery to log a sequence of status per request to a database. The documentation of the 2.4 version of Akka presents a state machine without using AbstractFsm and discusses the possibility to distinguish first events and then states or the other way round. In the AbstractFSM-DSL you're bound to discern states before events (and data). It can even be argued with some justification to abstain from the akka FSM altogether.

A more light-weight approach to build FSM utilizes become/unbecome, a nice presentation is here, an AbstractFSM vs. become/unbecome discussion can be found here. Closer visual proximity to business rules gives the major argument for the former.

Entering the more slippery terrain of judging the use of AbstractFSM for the task at had. I think, some things can be said, given the reading of the requirements is approximately ok.

The states form a linear chain, so the two "AbstractFSM-per-request" can be replaced by other per-request structures:

  • a single chain of actors, each of them representing one state

chain of actors

  • a single actor, sending events to itself, one event type per step shown above, emitting messages to the database writer at each point. Perhaps an enumeration would suffice as well.

The appeal of those versions may increase considering this: since the given implementation uses (at least) one FSM per request, the question arises, how the transition QUEUED->PROCESSING comes about (or discovery processing -> discovery done for that matter), and what QUEUED relates to a technical level. Items are never in a queue, always in an exclusive actor (on the other hand a QUEUED state would be more obvious in a single-FSM approach which actually uses a queue, but then the state doesn't apply to the actor, but to the item the actor handles). It is not obvious, which external event should cause that transition. Akka FSM is geared to describe deterministic FSM (see also this, giving the same argument for the opposite reasons), but if this transition is not triggered by an external event, the FSM must become nondeterministic and trigger its own epsilon-transitions ~ transitions not caused by any input. A rather complicated structure, probably implemented somehow like this:

onTransition(
    matchState(Initial, Queued, () -> {                    
        getSelf().tell(new Epsilon(), getSelf());
    }));

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser_mdaView Question on Stackoverflow
Solution 1 - JavaCuriosa GlobunznikView Answer on Stackoverflow