Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Handling "priority" message in actor framework

Solved!
Go to solution

Hi Guys,

 

I've got a quick question. I'm wanting to have an actor with a helper loop that's doing some long things in the background. These items "could" (but with only medium probability) generate something useful to the user. However, if the user sends something for processing, I'd like that handled immediately as that's highly likely to generate something useful to the user.

 

There are two options that I see, mind commenting on which is a better design? 

 

Option 1 -- register the single helper loop for a high-priority user event (in addition to high-priority stop event used to close the helper loop) -- possible race conditions...

 

Option 2 -- instead use two helper loops, one for the background events and one for high priority user events (both loops now only have one high-priority event, being the stop event)

 

Any others I should consider? 

 

Thanks,

 

-wavepacket


------------------------------------------------------------------------------------

Please join the conversation to keep LabVIEW relevant for future engineers. Price hikes plus SaaS model has many current engineers seriously concerned...

Read the Conversation Here, LabVIEW-subscription-model-for-2022
0 Kudos
Message 1 of 23
(1,797 Views)

How exactly does your actor work? Does it receive a message, then do some processing on the data (which takes non-negligible time), then send a message somewhere else?

 

Since you say you're doing the grunt work in a helper loop, I don't think you need priority messages. Priority messages won't interrupt another message anyway; they just go to the front of the line. So if you have a message that takes a long time to process, a high priority message appearing won't stop the in-process one, it'll just be the NEXT one to fire when it's done.

 

If you are indeed doing the time consuming stuff in a helper loop, then your main message receiver (the Actor Core) will be responsive to "Hey this is an important message, process it now" messages anyway. It'll just need a way to either interrupt the helper loop to process right now or, if the helper loop only takes a little time to do each task, then perhaps it could just wait for the helper loop to be done with whatever it is it's working on.

 

I guess we could use a little more info about the architecture of the program. Where does the helper loop get its data if the user isn't actively sending it things? Are they automated messages or something?

 

You'd only need new Priority messages if, for some reason, a bunch of messages were already enqueued to your Actor and you needed to preempt them.




Heads up! NI is moving LabVIEW to a mandatory SaaS subscription policy, along with a big price increase. Make your voice heard.
0 Kudos
Message 2 of 23
(1,773 Views)
Solution
Accepted by topic author WavePacket

Option 3 -- when a request happens, your Caller Actor launches a new Nested Actor that does one slow item in the background. The Nested Actor stops itself when the item is complete (results returned via Last Ack). The processing probably still happens in the Actor Core (so that you can send AF Stop, if needed), but the Nested Actor can be dumb and doesn't need to manage events/priorities - it just processes and returns.

 

- If the slow items must be sequential, the Caller can keep an internal list. You can put UI priority messages at the front if you want.

- If the slow items can happen in parallel (sounds like it, based on Option 2), the Caller can simply launch all requested items whenever they come in, then track which ones are still processing.

0 Kudos
Message 3 of 23
(1,771 Views)

@BertMcMahan wrote:

How exactly does your actor work? Does it receive a message, then do some processing on the data (which takes non-negligible time), then send a message somewhere else?

 

...


So specifically I'm creating an actor that sends queries off to a database. I'm planning on queuing up a bunch of background tasks which I'm not all that interested. However, if the user asks for something from the database, I do want to display that right now.

 

So my thought was to maintain two helper loops which could open two connections to the database. One of those loops would handle all the background stuff, and then the other one would run that priority query separately.

 


@BertMcMahan wrote:

...

If you are indeed doing the time consuming stuff in a helper loop, then your main message receiver (the Actor Core) will be responsive to "Hey this is an important message, process it now" messages anyway. It'll just need a way to either interrupt the helper loop to process right now ...


As an aside, I'm not actually sure how to interrupt an event that's in process of executing. I do now how to create a high priority user event, but that seems dangerous due to potential race conditions with my high priority stop message. 

 


@BertMcMahan wrote:

...

You'd only need new Priority messages if, for some reason, a bunch of messages were already enqueued to your Actor and you needed to preempt them.


This is precisely my use case.


------------------------------------------------------------------------------------

Please join the conversation to keep LabVIEW relevant for future engineers. Price hikes plus SaaS model has many current engineers seriously concerned...

Read the Conversation Here, LabVIEW-subscription-model-for-2022
0 Kudos
Message 4 of 23
(1,769 Views)

@OneOfTheDans wrote:

Option 3 -- when a request happens, your Caller Actor launches a new Nested Actor that does one slow item in the background...


So this suggestion interests me, particularly since my implementation is for a database which supports concurrent communication. If I have X queries to run to run, is there really a reason that I couldn't launch X copies of the nested actor and then have them all run in parallel? 

 

I'll also have to face the question of who should be shutting down the database actor(s). Does the database actor shut itself down after sending the query result, or should the callee manage how many copies are alive. I haven't had to face the music on that type of question before.


------------------------------------------------------------------------------------

Please join the conversation to keep LabVIEW relevant for future engineers. Price hikes plus SaaS model has many current engineers seriously concerned...

Read the Conversation Here, LabVIEW-subscription-model-for-2022
0 Kudos
Message 5 of 23
(1,758 Views)

Ah, good to know then!

 

So, cancelling operations can be done one of two ways. The first is to do it manually, and have any process periodically check to see if it needs to stop, then stop itself. For example, you could set up a Stop notifier, then at the beginning of each loop, run Wait on Notification with a timeout of 0. If there's a Stop notification there, it stops, otherwise it continues doing its thing. (There are a million ways to do this).

 

Option two is the nuclear one- you simply abort the VI or process. You get a reference ahead of time, then if you need to stop it, you kill it. This is generally to be avoided since you will exit in an unknown state.

 

In your case, you're doing database calls. I assume each call takes some finite amount of time. If that's all your actor is doing, then I think I'd process each request within the message- as in, without a helper loop at all. If you add a helper loop, then you'll need some way to enqueue the database requests between the actor core and the helper loop, then you'll need to have a regular message send a Priority message to the helper loop (as in, enqueue at front of queue) to get the results right away. This sounds like more work than just letting the AF message queue handle that for you. Each message does one database query. If the user wants something now, send it as a priority message.

 

This will get you everything except for interrupting a current request. How long do these background requests take? Is it possible to just wait until the current one has finished? If not, you'll have to find a way to cancel the request. I don't do much with databases but basically your toolkit will need to let you cancel an active request, which might be a little hard to do.

 

Thus, we get to the last option, which you did mention- dual loops. You could do this one of two ways; either two helper loops, where one is the "fast" one and one is the "background" one, but you still need to implement internal queues, or you could just launch two actors and let AF handle the queues for you. Store one enqueuer as the "background" one and one as the "user" one. They can both open handles to the same database, assuming your toolkit supports it.

 

How long is each database request? How many user requests do you anticipate happening at once? Would two actors work? You can make them the exact same actor, just launched twice.




Heads up! NI is moving LabVIEW to a mandatory SaaS subscription policy, along with a big price increase. Make your voice heard.
0 Kudos
Message 6 of 23
(1,756 Views)

@WavePacket wrote:

@OneOfTheDans wrote:

Option 3 -- when a request happens, your Caller Actor launches a new Nested Actor that does one slow item in the background...


So this suggestion interests me, particularly since my implementation is for a database which supports concurrent communication. If I have X queries to run to run, is there really a reason that I couldn't launch X copies of the nested actor and then have them all run in parallel? 

 

I'll also have to face the question of who should be shutting down the database actor(s). Does the database actor shut itself down after sending the query result, or should the callee manage how many copies are alive. I haven't had to face the music on that type of question before.


The particulars will depend on your exact database setup and processing power, but I bet there's a balance between parallel queries and serial ones. You could do some benchmarking to figure that out.

 

Shutting down the actors depends on how long they're active and what the init/deinit is. Some thoughts to consider:

 

-If an actor only lives to launch, do a single query, then die, do you need an actor? Just use an async subVI with an enqueuer input. An actor works too of course.

-If you want your actors to persist, I'd try to benchmark your system and decide on a number of actors to use. Create another intermediate actor to manage message routing and actor life ("Database actor manager") which can launch n actors, then submit each new query to the next one in the list.

 

There's no single right answer for single-use vs. long-term actors. I've used both. Single use are generally easier to use since they don't have a state and get wiped each time you use them, but they are usually slower since you have to acquire resources on each call.




Heads up! NI is moving LabVIEW to a mandatory SaaS subscription policy, along with a big price increase. Make your voice heard.
0 Kudos
Message 7 of 23
(1,750 Views)

@BertMcMahan wrote:

...

 

How long is each database request? How many user requests do you anticipate happening at once? Would two actors work? You can make them the exact same actor, just launched twice.


So in this case, I could split the queries into manageable bites which should take less a second to return, that's probably tolerable to handle within the message. I don't anticipate having many user requests happening per second. Interesting...


------------------------------------------------------------------------------------

Please join the conversation to keep LabVIEW relevant for future engineers. Price hikes plus SaaS model has many current engineers seriously concerned...

Read the Conversation Here, LabVIEW-subscription-model-for-2022
0 Kudos
Message 8 of 23
(1,748 Views)

@BertMcMahan wrote:

@WavePacket wrote:

@OneOfTheDans wrote:

Option 3 -- when a request happens, your Caller Actor launches a new Nested Actor that does one slow item in the background...


So this suggestion interests me, particularly since my implementation is for a database which supports concurrent communication. If I have X queries to run to run, is there really a reason that I couldn't launch X copies of the nested actor and then have them all run in parallel? 

 

I'll also have to face the question of who should be shutting down the database actor(s). Does the database actor shut itself down after sending the query result, or should the callee manage how many copies are alive. I haven't had to face the music on that type of question before.


The particulars will depend on your exact database setup and processing power, but I bet there's a balance between parallel queries and serial ones. You could do some benchmarking to figure that out.

 

Shutting down the actors depends on how long they're active and what the init/deinit is. Some thoughts to consider:

 

-If an actor only lives to launch, do a single query, then die, do you need an actor? Just use an async subVI with an enqueuer input. 

...


With regard to whether this needs to be an actor, I'll have to admit that I'm not sure. I haven't done much with async subVIs before...what are the pros/cons between this method and actor framework?  


------------------------------------------------------------------------------------

Please join the conversation to keep LabVIEW relevant for future engineers. Price hikes plus SaaS model has many current engineers seriously concerned...

Read the Conversation Here, LabVIEW-subscription-model-for-2022
0 Kudos
Message 9 of 23
(1,741 Views)

I don't use them much either to be honest, but the pro to using a VI is that there is very little overhead and it's very easy to debug. It just launches and there ya go. If you're already set up in AF then you can use an actor this way but it's a little bigger since you need to make an Actor class and either override Actor Core or create at least a single message. It is nice that you can put initialization stuff in Pre launch init and know whether or not it got the resource right away.

 

I just wanted to throw it out there. I know when I'm in an AF project I tend to want to make EVERYTHING an actor, and often that's just needless overhead if a function just needs to be called and then return. I don't think there's any harm to using it this way.

 

Don't forget though- no matter which way you choose, make sure the VI that's interacting with the DB is set to reentrant. Non-reentrant VI's will be shared amongst actors and will serialize everything.




Heads up! NI is moving LabVIEW to a mandatory SaaS subscription policy, along with a big price increase. Make your voice heard.
0 Kudos
Message 10 of 23
(1,732 Views)