Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Actor Framework favorite (mis?)use cases


@m3chtroid wrote:

That said, it is a common pattern in the framework that an actor wants to send a message to its caller with the express purpose of the caller relaying that message elsewhere.


I want to restate this sentence.

 


That said, it is a common pattern in the framework that a programmer wants an actor wants to send a message to its caller, which the programmer designs with the express purpose of the caller relaying that message elsewhere. 

Multi-hop communication is the intent of the programmer. An actor may or may not intend that, and my contention is actors should never intend that. I believe we do our software harm when we conflate our goals for the system overall with the goals of a single module.

 

It's interesting that you pick "Last Ack", since that's a message that cannot just be bubbled up to the next layer up. It's contextual: the next layer up wouldn't know what to do with a "Last Ack" message for a nested actor that it did not launch. "Stop" would be a better choice for this point.

 

Doing better job of autogenerating the code for "receive A from nested means turn around and send B to caller" or vice versa.

0 Kudos
Message 21 of 33
(1,907 Views)

You're right, I mixed up last ack and stop when going through the material.  I knew it had to do with an actor stopping on error, but I misremembered.  Also, I have no idea what that last sentence is trying to convey.

 

But I want to back up for a second.  When I originally specified the metadata as being a GUID-like unique identifier, that was with the purpose of profiling.  If you want to systematically study the overhead of a tree messaging structure, it's easiest to have a general framework that allows you to send a, well, "ping request" to a specific member of the tree. 

 

I want to go back to a much earlier comment, though.


@AristosQueue (NI) wrote:

When Alpha and Zed are on opposite sides of the tree, Alpha cannot request anything of Zed without knowing about Zed. But knowing about Zed implies a design of Zed's caller, and Zed's caller's caller, and all the way up the tree until the common caller. And those assumptions may need to change such that Zed doesn't even exist. What if Zed's caller chooses not to launch Zed but instead just incorporates Zed's activity into itself? Or splits Zed into two separate actors? Alpha has to be edited to take those changes into account.

 

My working hypothesis: Alpha does NOT need to know anything from Zed directly. Not ever. What it needs to know is some aspect of the environment. It happens that Zed is what is computing that aspect, but Alpha shouldn't know that. The only environment that Alpha knows and can trust is the actor that created it and gave it purpose, it's own caller. Passing the message up from Zed and back down through the tree will actually change the message at each step. That is a good and desirable thing.

It's this part that I take issue with.  The metadata, the routing, the index, all that is directly aligned with what you said here.  Metadata doesn't have to imply the existence of Zed, only that someone can do zed's job.  The metadata doesn't have to be a specific actor or route, what I'm trying to say here is that the metadata can be the requested aspect of the environment itself. Since the routing is explicitly handled each step of the way, if Zed's caller incorporates Zed's activity into itself?  Well, they were already responsible for routing Zed's mail. They just accept the message, and send the response themselves.

 

What I don't get is what requires something as simple as a request about an aspect of the environment requires a change in the message at every step.  Don't get me wrong, I also think every step should have the possibility of reinterpreting the message, but I'm unsure what we, as programmers, get out of message reinterpretation being required with each and every step through the tree.  Am I fundamentally misunderstanding something, or isn't it that very requirement that every hop of a message be transformative what's really making sending a message imply "a design of Zed's caller, and Zed's caller's caller, and all the way up the tree until the common caller"?

0 Kudos
Message 22 of 33
(1,896 Views)

@m3chtroid wrote:

I'm unsure what we, as programmers, get out of message reinterpretation being required with each and every step through the tree.  


Reusability and Maintainability

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
Read about my thoughts on Software Development at sasworkshops.com/blog
GCentral
0 Kudos
Message 23 of 33
(1,891 Views)

@Taggart wrote:

@m3chtroid wrote:

I'm unsure what we, as programmers, get out of message reinterpretation being required with each and every step through the tree.  


Reusability and Maintainability


More specifically, requiring reinterpretation forces actor decoupling.  And I don't think it's correct to say reinterpretation is required; rather, I think reinterpretation just always happens, regardless of your intent.

 

A does not send a message to C through B.  A sends a message to B, then B sends one to C.  This is true even in the degenerate case, where B simply copies the data from A into the message for C.  Let's look at the hypothetical temperature measurement.  Let's say A reads a set of thermistors, B controls for temperature, and C displays temperature in a UI.  Same value - 75 degrees - at every step.  But the messages unavoidably mean something different.  The message from A is a declaration that the thermocouple has measured a temperature of 75 at its location.  The message from B is that the calculated room temperature is 75.  Those aren't the same thing, although they appear to be.  Maybe there is no calculation.  Maybe B will apply a scaling value to the reading.  C doesn't know, and shouldn't care.  A doesn't know how its data will be used, and shouldn't care.

 

Of course, you, the developer, have deep knowledge of what is supposed to be going on.  But coding on the assumption that that actors don't (or at least should not), helps you avoid building inadvertent dependencies between the actors.

0 Kudos
Message 24 of 33
(1,865 Views)

Aha!  And there I see how fundamentally looking at the same thing was giving me a different outlook.  I'm used to the same idea, except kind of the inverse?  The UI gets a message saying the temperature is 75 degrees.  It gets the message relayed to it from a room controller, so it knows that message is about the room temperature.  The room controller had gotten the same message (this is the difference, our messages were of the same class, same type), but since it's getting it from a thermsistor child, it knows the data is about that thermsistor's measurement.  Now the room controller could have done something to the message by intercepting it in the process of routing, including just not passing it on (and in your example, instead updating its internal average, waiting to send a new message according to some request or internal timer).

 

Same actor decoupling, A doesn't know how its data will be used, it's simply reporting the weather.  C doesn't know where the temperature came from (beyond its direct messenger, B) it's just displaying the data.  I see now we were actually describing nigh identical message structures at their core, I was just moving the implicit contextual info from the class and typing of messages sent between A, B, and C to the API documentation that dictates how a singular, shared message+metadata pairing should be understood and used.

 

0 Kudos
Message 25 of 33
(1,860 Views)

For many years Steven has been trying to coax us to his side and I think his recommendations are lost on many of us (or we just don't have the time/energy/know-how to implement them). That's why I fantasize about a presentation that shows me a few example problems and the UML for the initial gut-reaction solution vs. the UML for the improved solution which has a tree dependency structure that doesn't have actors relaying the same message up and down the tree.

 

Right now I'm staring at a UML diagram created by a different architect for an application that I have to help implement. It breaks from the tree dependency structure and I can't help but wonder if there's a better way.

 

Depending on the project, there can be a lot of pressure from stakeholders to deliver working software. Because most people who use technology are accustomed to encountering bugs (in phones, cars, devices, new releases of LabVIEW, etc.), it is normal to release something buggy and fix it later (i.e. in SP1). So given the option to release something in a week that might have bugs versus something in two weeks that probably has fewer bugs, it's really tempting to choose the first option. I think most of us would prefer to work on projects where the customer says, "Slow down, relax, take your time, shore up your processes, and make sure you don't cut any corners." For most of us that's not reality. From our past experiences we have developed enough hubris to start a new project and think we know what corners we can get away with cutting. We live by general Patton's words, "A good plan, violently executed now, is better than a perfect plan next week."

0 Kudos
Message 26 of 33
(1,830 Views)
Hi all
 
AQ, essentially, I think the point you (and others) have made with:
 
My contention is that the rewriting-for-higher-level that we naturally do for error returns should apply to ALL returned values, especially in asynch systems.

...and the related data-agnostic discussions is the most pertinent one for me. I do, however, contest the part of "A" having to be "edited to take those changes (in Zed) into account", dismissing it with proper decoupling between the two.
 
In fact, I think that as we are discussing architecturetopology, see if this makes sense: in my view "A" should know that he needs to request "something" from actor "Zed", but has no idea where Actor Zed sits in the application messaging topology. Maybe Zed is just next to it, or maybe not. The more decoupled actors are in an application, the more agnostic they can be in the services (jobs?) they provide/request and where they actually stand in the topology. It was my impression that this would promote (possibly be an effect of) decoupled code.
 
At this point I'm seeing this discussion more as "what are the best message/application topologies" (like I believe @justACS said) over how the AF itself. At that point I guess it becomes more of a general computer science domain, and maybe developers with multidisciplinary experience can pitch in. I would be curious how different developers from different fields would structure their application (which goes in line with what others have said)
 
All in all, I think AQ sums it up nicely with
 
Multi-hop communication is the intent of the programmer. An actor may or may not intend that, and my contention is actors should never intend that. I believe we do our software harm when we conflate our goals for the system overall with the goals of a single module

At the beginning of this discussion I confess I didn't see this, but I for one am taking your advice and thinking about it - even if unfortunately I don't see it yet 🙂
 
On another note, let me reiterate that AF in LabVIEW is indeed my preferred workflow in LabVIEW, and I am very grateful to all for it. I have absolute confidence that it does scale and so far it fits whatever demanding or large challenges I've faced with elegance.  
 

0 Kudos
Message 27 of 33
(1,785 Views)

AQ, if I understand you correctly, then for a truly asynchronous system a "delegation of authority" is what is best used in practice with AF. 

 

This is typically how I develop, I have Actor A that is the root level actor...it spins up Actor B for some task (IO, file interaction). You probably would not want Actor B to actually handle the task itself, but be a "controller" of sorts for the action. Actor B would then spin up Actor B1 to actually handle the task and interact with the IO or file so that B is then free to handle other requests and spin up B2...B3....etc. 

Of course with a shared resource handling IO, file, etc. can get a little "hairy".

 

Is a "delegation of authority" the correct way to develop AF for a true asynchronous experience? 

Appreciate constructive criticism, it is there to help you succeed.
0 Kudos
Message 28 of 33
(1,738 Views)

@SHowell-Atec wrote:

Is a "delegation of authority" the correct way to develop AF for a true asynchronous experience? 


I'll go with "a" correct way, not "the".

 

Single Responsibility Principle has value in any software, AF or not, so therefore it has value for AF. But there are several systems I've seen with tightly coupled actors that are deeply enmeshed with each other but nonetheless achieve good parallelism. Whether or not those systems have other problems resulting from their coupling is a separate question. But they can be successfully asynch.

 

I will say that the delegation of authority for actors is -- in my opinion -- the easiest pattern to teach and the easiest to understand when I'm looking at code I didn't write.

Message 29 of 33
(1,726 Views)

A classic example: often in distributed systems, Actor A needs data (or something) from Actor Zed, all the way across the tree. As routing the message up/down the tree doesn't scale, often we see actors being passed the enqueuer of some "service" actor and communicating directly. 

There have been ample discussions on this (for example). I would argue its also present in some less obvious aspects like described in the AF whitepaper "How to use the AF" (see part on self addressed messaging). Personally, I agree with what was said previously by some better devs than me: prefer the tree, but accommodate the possibility of actors being imbued/having-a-mandate to communicate with some other actor in the application. This is more obvious in terms of larger applications where there are bound to be "service" Actors (central error handling, configuration managers, Broker/servers, etc).


Hello crandiba,

 

The between actor communication was one of my main concerns when started developing in AF. It was a real pain in the beginning the high coupling between actors. This really changed when we got to know Abstract Messages. This is the real key in decoupling actors and keeping the single responsibility principle intact.

 

So we designed an abstraction for the Caller actor for handling these messages coming from nested actors. Now we have applications with many layers of communication (I'd say even complex layers) "easily" implemented without breaking the actor tree only using two VIs for enqueuers "Read Self Enqueuer" and "Read Caller Enqueuer", this is all you need for sending messages. Hopefully I'll present this solution in NI Week this year, so you can get to know.

 

Nowadays we develop new modules (actors, libraries) always taking into account this model and it results in very high code reusability.

 

Regards,

 

Felipe Pinheiro Silva


Follow my blog for LV content!

0 Kudos
Message 30 of 33
(1,691 Views)