Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

The Realities of Hierarchical Messaging

Solved!
Go to solution

Before I stumbled across the Actor Framework (and actor-oriented design in general), my messaging systems were simple point-to-point types. I happily made message queues available to spawned processes all over my wire-scape, and (aside from the occasional tricky race condition) life was good.

Now, having developed a few smallish Actor-based applications and in the midst of refactoring one larger project to get some of the benefits of this new approach, I have some questions about the realities of Hierarchical Messaging.

I'd love to get some opinions about the best ways to propogate messages from one branch of a tree to another. Consider the following example:

hierarchy.PNG

My Main actor launches a UI actor and a Camera actor, which in turn launches a Camera Driver actor. Now imagine I have a button on my UI to reset the camera driver. Assuming I don't want to create any direct paths from the UI to the driver:

  • What is the quickest (= least development work) way to get the reset message to the driver?
  • What is the lightest (= least code) way?
  • What is the best way, assuming I have unlimited wires at my disposal?
  • Are there any really bad ways to do it?

I'm hoping that there are more elegant ways than creating a Reset Driver message in each of the Main/Camera/Driver actors and then passing my command along the chain.

Has anyone developed a more general message routing system to traverse through the hierarchy using a single Send method, or does that go against the spirit of things?

Would love to hear some real-world experiences about how people manage this...

--
Chris Virgona
0 Kudos
Message 1 of 7
(3,515 Views)

Barring yet another link to existing discussion threads on this topic, here's some direct advice based on experience from my current project:

First: If your application logic is not going to be presented by multiple different UIs, I don't see the point of having a "Main" actor. Just let the UI actor launch and control the Camera actor. That removes one set of Status/Cmd message pairs that would otherwise have to travel through Main.

Note: the following text assumes you've made that change.

In response to your questions about sending messages from a button on the UI to the Driver:

  • The best way  is to send a Cmd message from the UI to the Camera with high-level information, then to have the Camera use its encapsulated low-level information about the Driver to send a more specific Cmd message (or a properly ordered sequence of msgs).
  • If the msg from the UI button  contains context-free data without any kind of API command attached to the data, then it's completely safe to send a Data msg from the UI directly to the Driver. This may not save you any coding, though: it requires that the Driver's NQR (that's my shorthand for the difficult-to-type "Enqueuer") be sent to the UI by the Camera when Camera launches Driver. That NQR has to be stored by UI, and its reference has to be updated in a Status msg from Camera to UI any time the Driver is stopped and relaunched. So while it lets you avoid creating packages of forwarded msgs, it requires you to create extra msgs for maintaining the UI's "subscription" to the Driver.
    • Creating a direct cross-tree link for Data msgs establishes tight coupling between the two actors. You can minimize this by letting the UI actor send an abstract msg that defines the data, then defining a concrete msg (inherited from the UI's abstract msg) for the Driver to accept. The concrete msg defines the API method to call for the Driver's acceptance of that data. I want to create a small document that goes into detail on this aspect of messaging design, but I'm in a busy cycle on my project at the moment.
    • If the msg from the UI button contains any kind of command or intelligent direction about what to do with the data it carries, then the only appropriate way to send that msg is through the actor tree. This maintains appropriate encapsulation of state information and prevents directly coupling two actors that would otherwise know nothing about one another.
  • There are lots of really bad ways to do it. In general, I recommend following the advice of people like Daklu and Dmitry (and more recently, drjdpowell) who categorize inter-actor msgs based on their purpose:
    • Cmd/Req: these messages call directly into the API (public methods) of an owned actor and should only be sent down the tree to direct sub-actors. If a cmd needs to reach a sub-actor two generations down the tree, it should be forwarded. It may be useful to label these messages as "Request" instead of "Command" in order to remind yourself that actors always have the option of ignoring a message based on their current state.
    • Status/Event: these messages call back up the tree to notify a caller of some occurrence that could have an effect on the caller's internal state. Because good actor-oriented designs dictate that no actor should manage any part of the state of its caller, you have to notify the caller of things that occur but that do not affect your own state.
      • E.g. if your UI had a subpanel with more buttons for configuring advanced options in the camera driver, that subpanel should fire Event msgs to the UI actor notifying it that a button was pressed. Since part of the UI's state is the Camera actor, it then decides which Cmd msg to send to the Camera in response to that "Btn Pressed" event. (The Camera then forwards the appropriate low-level Cmd msgs to the Driver.)
    • Data: these messages pass data to other actors without any knowledge of what each recipient might do with that data. The successful delivery of the data to any recipient cannot be assumed and therefore should not be cared about. (If you need to guarantee successful delivery of some data, then you're really working with a Cmd msg.) Because senders of these messages have no knowledge of how their recipients work, Data msgs can be safely sent across the tree. It's common to structure the send/receive mechanisms for these msgs as a Publish/Subscribe network. I have a nice library that bolts onto AF to provide pub/broadcast/unsub behavior for any Data msg, but it needs a custom message scripting tool before I can share it with the community.

There's a starter. I hope to write about this stuf in lots more detail when I get a lull in project work.

0 Kudos
Message 2 of 7
(2,163 Views)

I would have a "Reset Camera" message, or so. Then the Camera actor can decide to call "Reset Driver".

Message 3 of 7
(2,163 Views)

> I'm hoping that there are more elegant ways than creating a

> Reset Driver message in each of the Main/Camera/Driver

> actors and then passing my command along the chain.

Passing your command along the chain is the good way to do it regardless of anything else. Look at the Stop Msg that is part of the core of the framework. The message passes through the tree. Each level hears the stop message, reacts to it, and passes it along to its nested actors. This "passing along" is an important part of actually getting a clean shutdown. Your Reset message is pretty much the same. You want each level of the hierarchy to hear some sort of reset command, do whatever they need to do, and then pass along to the nested actors so they can do the same. In fact, at some levels, you might not send a single reset command but a whole stream of commands, all of which are designed to put the system back in a cleaned up state.

The only way to get around this would be to create a direct path from the UI to the driver which -- rightfully -- you are loathe to do.

The lightest way would be if all of your actors inherit from a single common parent. Give that parent a Reset method and create a message that invokes it. Now you can send that same message class to any of the child classes. Essentially, exactly what the Stop Msg does. This approach works as long as you don't need to do any complicated "reset child, if that fails, try this other idea, if that fails, try something else..." because that's when you need a whole reset protocol to correctly spin down a system. It also falls appart if any of your actors don't have a well defined "reset" on their own: imagine a drill that is deep in the earth, drilling. The "reset" command at the high application layer actually means "back yourself out back to the surface." But the drill itself doesn't have its own navigation on board, that's in the caller actor that sends drive instructions to the drill. When the navigation actor gets the reset message, it starts sending the complex, and dynamic, set of messages needed to back up the drill and turn it and twist it to get it back to the surface. You wouldn't want the drill actor to even be able to get a reset message directly because the command is not well defined for the drill actor itself.

Message 4 of 7
(2,163 Views)

Todd_Lesher wrote:

I would have a "Reset Camera" message, or so. Then the Camera actor can decide to call "Reset Driver".

That's what I have been doing until now, although it still necessitates an "dummy" message in the main actor just to pass the Reset message through...  (Is there a name for such beasts, whose sole purpose is to relay a specific message to a specific destination?)

--
Chris Virgona
0 Kudos
Message 5 of 7
(2,163 Views)
Solution
Accepted by topic author fabric

AristosQueue wrote:

... In fact, at some levels, you might not send a single reset command but a whole stream of commands, all of which are designed to put the system back in a cleaned up state.

Yes, when the message is general enough there may be certain economies of scale from traversing the entire hierarchy. Obviously a Reset message will often fall into this category. (That's the danger of posting a specific example!)

The lightest way would be if all of your actors inherit from a single common parent. Give that parent a Reset method and create a message that invokes it. Now you can send that same message class to any of the child classes.

Ok, so then I simply override the Reset method at each point along the chain, right? Just to spell it out:

  1. UI sends Reset message to Main.
  2. Reset method in Main relays Reset message to Camera
  3. Reset method in Camera relays Reset message to Driver
  4. Reset method in Driver does the work...

That sounds pretty workable. (I think the best part is that I only need one message class...)

Thanks for helping! 🙂

--
Chris Virgona
0 Kudos
Message 6 of 7
(2,163 Views)

fabric wrote:

Todd_Lesher wrote:

I would have a "Reset Camera" message, or so. Then the Camera actor can decide to call "Reset Driver".

That's what I have been doing until now, although it still necessitates an "dummy" message in the main actor just to pass the Reset message through...  (Is there a name for such beasts, whose sole purpose is to relay a specific message to a specific destination?)

I have to maintain flexibility within components so they can be used in different places. It doesn't buy me anything to come up with specific message classes that can be handled by every component. Everything is about the interface between components. Yes, some components couple more than others.

0 Kudos
Message 7 of 7
(2,163 Views)