We're looking for some advice/opinion on the best way to construct our 
actor using the FSM trait. I've given a bit of background, hence the long 
post, hopefully it won't be too confusing.

We writing an actor that aggregates data from three sources, the third of 
which requires the data from the first two. We've been looking at the FSM 
syntax as opposed to the standard receive/become syntax as we had an actor 
which did a similar thing, and the flow was a bit unclear; we're finding 
FSM makes the states/transitions clearer.

Our actor does the following on a request for a "Thing".

   - Simultaneously tell ThingPartAProvider and ThingPartBProvider to get 
   part A and part B of the Thing respectively.
   - Combine thing part A and part B to create a single Thing once both 
   providers have told us the results.
   - Send the Thing to a the third source, the ThingEnricher and wait for 
   the enriched thing
   - Finally send the resulting thing back to the original requester

We've thought-up two proposed state machines for solving this problem, and 
sketched out the implementation, and neither seem totally satisfactory, 
I've attached a diagram.

<https://lh3.googleusercontent.com/-3_pd5yJqRPc/U3Id0ZXX1uI/AAAAAAAAEqk/tcn17jzt84o/s1600/FSM.png>


*FSMv1:*
An partial implementation (for the states waiting for parts A and B) would 
look something like this:

-----
  when(WaitingForPartAandPartB) {
    case Event(PartA, _) => goto WaitingForPartB using PartialThing(PartA)
    case Event(PartB, _) => goto WaitingForPartA using PartialThing(PartB)
  }

  when(WaitingForPartA) {
    case Event(PartA, _) => goto WaitingForThingEnricher using 
PartialThing(PartA, PartB)
WaitingForThingEnricher  }

  when (WaitingForPartB) {
    case Event(PartB, _) => goto WaitingForThingEnricher using 
PartialThing(PartA, PartB)
  }

  onTransition {
    case WaitingForPartA -> WaitingForThingEnricher |
         WaitingForPartB -> WaitingForThingEnricher => nextStateData
      thingEnricher ! PartialThing(PartA, PartB)
  }
-----

*Advantage*: The only events causing state changes are those coming from 
outside the state machine, and the flow stays in a single, forward 
direction.
*Disadvantage*: The duplication of the WaitingForThingEnricher goto 
statements, and that this would be become a lot more complex if a PartC was 
added.

*FSMv2:*
An implementation for the states waiting for parts would look something 
like this:

-----
  when(WaitingForPartAandPartB) {
    case Event(PartA, PartialThing(None, partB)) => {
      self ! CheckForBoth
      stay using PartialThing(Some(PartA, partB))
    }

    case Event(PartB, PartialThing(partB, None)) =>  {
      self ! CheckForBoth
      stay using PartialThing(Some(partA, PartB))
    }

    case Event(CheckForBothParts, PartialThing(Some(partA), Some(partB))) =>
      goto WaitingForThingEnricher using PartialThing(Some(partA), 
Some(partB))
  }

  onTransition {
    case WaitingForPartAandPartB -> WaitingForThingEnricher => 
nextStateData match {
      case thing @ PartialThing(Some, Some) => thingEnricher ! thing
      case _ => // stay
    }
  }
-----

*Advantage*: The exit from this state is a single point, and that it allows 
for additional parts by adding one extra event and one extra option to the 
state. 
*Disadvantage*: Sending a message to self is perhaps a bit confusing at 
first glance, and feels an odd thing to do.

What I'd really like to do in this implementation is move that out to the 
transition to make it a bit cleaner like this:

-----
  when(WaitingForPartAandPartB) {
    case Event(PartA, PartialThing(None, partB)) =>
      goto WaitingForPartAandPartB using PartialThing(Some(PartA, partB))

    case Event(PartB, Thing(partB, None)) =>
      goto WaitingForPartAandPartB using PartialThing(Some(partA, PartB))

    case Event(CheckForBothParts, PartialThing(Some(partA), Some(partB))) =>
      goto WaitingForThingEnricher using PartialThing(Some(partA), 
Some(partB))
  }

  onTransition {
    case WaitingForPartAandPartB -> WaitingForPartAandPartB => self ! 
CheckForBoth
    case WaitingForPartAandPartB -> WaitingForThingEnricher => 
nextStateData match {
      case thing @ PartialThing(Some, Some) => thingEnricher ! thing
    }
  }
-----

But this does not seem possible (yet? I found this issue 
https://github.com/akka/akka/issues/13970 to allow it?)

Has anyone else had experience in writing actors that did a similar 
aggregation? What approach did you find best? Is there a completely 
different approach we should be considering, or which of these two would 
you consider best?

Thanks,
tz.lillington

-- 
>>>>>>>>>>      Read the docs: http://akka.io/docs/
>>>>>>>>>>      Check the FAQ: 
>>>>>>>>>> http://doc.akka.io/docs/akka/current/additional/faq.html
>>>>>>>>>>      Search the archives: https://groups.google.com/group/akka-user
--- 
You received this message because you are subscribed to the Google Groups "Akka 
User List" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/akka-user.
For more options, visit https://groups.google.com/d/optout.

Reply via email to