ANALYSIS: Aware behind the scenes. Context. Passing BOs.

If you have questions or if you want to share your opinion about Aware IM post your message on this forum
Post Reply
Jaymer
Posts: 1889
Joined: Tue Jan 13, 2015 10:58 am
Location: Tampa, FL
Contact:

ANALYSIS: Aware behind the scenes. Context. Passing BOs.

Post by Jaymer »

TLDR; Complex rules in an Aware Process are hard for Aware to evaluate correctly,
So add more sub-Processes to simplify a "parent" Process,
At what overhead cost to start subprocesses?


(feel free to leave comments!)

This post came about due to discussions about Aware not being able to handle complex IF/THEN/ELSE Rules in a Process.
This post
and Mark Bailey here.
Basically, if the Rule will let you enter in 6 IF/THEN/ELSE blocks and it has an internal runtime error where it cannot produce the correct answer, then its a bug. And I found a way to re-write my rule[s] so that it did give the right answer, but at what (if any) performance penalty.

This entire post was my reply to a post here.
(which was offering a solution where we make MORE processes in Aware (we have to make plenty already, so I'm not a fan).

And it got me thinking about what goes on in Aware when we call Processes - I always wonder what [Negative] effect is calling a subProcess going have if I pass it 3 or 4 BOs - is it ok? Should I re-write or do something a different way? Should I even worry about it at all? Does it use more memory to pass several big BOs? etc etc


So I'm removing it from that post for discussion here - so there's an awareness of these internals for people to consider when designing your systems.
(explanation of this in the next post in this thread)

The Limitation in Aware of having only 1 IF/THEN/ELSE per Rule is a pain.
Example:
Desired

Code: Select all

Rule1:  IF State = FL THEN
..... bunch of stuff here
IF AccessLevel="Admin" THEN WriteFLORIDALog 
...is not allowed IN a SINGLE RULE.
OK, so this isn't the end of the world. Not worth abandoning Aware for. I'm sure we can AGREE to settle on Option1 or Option2, right?

You could write it in a Single Rule like this:
Option1

Code: Select all

IF State = FL and AccessLevel="Admin" THEN .... bunch of stuff here... WriteFLORIDALog
ELSE
IF State = FL THEN .... bunch of stuff here
Or, You have to break it into 2 rules in the Process:
Option2

Code: Select all

Rule1: IF State = FL and AccessLevel="Admin" THEN .... bunch of stuff here... WriteFLORIDALog
Rule2: IF State = FL and AccessLevel<>"Admin" THEN .... bunch of stuff here
1st Discussion:
One main issue I have with both options is that I have physically copied my complex code 4 times in this process, instead of 1.
the "bunch of stuff here" could be this, for example:

Code: Select all

FIND Contacts WHERE Contacts=TicketE.ps_Contact1
CREATE NP_Print_Header WITH NP_Print_Header.DeptID=RO.ps_Dept.ID
Calc_Contact_Print_Headers_Estimate

EXPORT DOCUMENT RO_Estimate TO FILE SystemSettings.DocumentPath+'RO'+RO.RONo+'\Est_'+TicketE.TicketNum+'_1.PDF'

CREATE OutgoingEmail_to_Multiple WITH OutgoingEmail_to_Multiple.pm_Recipients=TicketE.ps_Contact1,OutgoingEmail_to_Multiple.Type='E',OutgoingEmail_to_Multiple.State='Queue',OutgoingEmail_to_Multiple.ps_RO=RO,OutgoingEmail_to_Multiple.Message=UNDEFINED,OutgoingEmail_to_Multiple.Subject='Repair Order Estimate for RO # '+RO.RONo,OutgoingEmail_to_Multiple.Attachment1_Path=SystemSettings.DocumentPath+'RO'+RO.RONo+'\Est_'+TicketE.TicketNum+'_1.PDF',OutgoingEmail_to_Multiple.Link1=`/logonOp.aw?e=`+ENCRYPT_B64(`domain=AAA&userName=`+Contacts.LoginName+`&password=`+Contacts.Password_Clear_Temp+`&testingMode=false&firstCommand=startProcess2,Estimate_Approve_OR_Reject,TicketE,`+TicketE.ID+`,main`)
log2 Contacts.LoginName

IMPORT DOCUMENT OutgoingEmail_to_Multiple.Attachment1 FROM SystemSettings.DocumentPath+'RO'+RO.RONo+'\Est_'+TicketE.TicketNum+'_1.PDF'
And I've been using a simple "State = FL" - when really thats something more realistic like:
IF TicketE.ps_Contact1 IS DEFINED AND TicketE.ps_Corp.PORequiredYN='Yes' AND TicketE.sc_Status='NEW' and AccessLevel='Admin'
MODIFY this with a NOT(), an OR or 2 of these in Parens and it gets complex fast.

Point is, to do Option1 or Option2 (which we agreed are perfectly good alternatives, didn't we?) now requires me to copy this large block of code 4 times - which makes it VERY easy for me to screw something up - and hard to maintain because whatever I did in that code needs to be changed in 4 places when the boss wants a change.
--> No reason to argue that all that code would be taken into a subprocess - cause thats the purpose of this whole thread anyway. Just go with it.

2nd Discussion:
The alternative post (way up at the top) would have me restructure my Process to say:
IF STATE=FL then Process_1
ELSE
IF STATE=TX then Process_2

(of course, with my more realistic test like this:
IF AccessLevel='Admin' AND TicketE.ps_Contact1 IS DEFINED OR ( TicketE.ps_Corp.PORequired<>'Always' AND TicketE.sc_Status='NEW' )
it becomes more difficult to do this anyway.

Anyway, that what starts the more detailed analysis below where I examine whats happening "overhead-wise" when I call another process.


3rd Discussion:
Option2 isn't that bad, but if I have lots of those, then the absence of a BREAK function to stop further processing in this Process makes a LOT more Rules/Rule Expressions have to be unnecessarily evaluated (more below).

TLDR 2 - All of this because Aware can't handle a complex IF/THEN/ELSE rule.
The Desired way, if it worked, wouldn't have me duplicate ANY code.





... and the inability to "BREAK" out of a Rule doesn't help.
In my complex Process in this post, a BREAK would help once the TRUE "IF" branch was found, hopefully saving time by not having to execute further down the Process. When I simplified my Process to 3 separate Rules, the BREAK would exit and prevent further unnecessary tests once the condition was met. So It wouldn't always be all 3 rules executing. In fact, I'd put my "most common" condition first so the majority of the time Aware would never see the 2nd or 3rd Rule because the Break would [gracefully] end the Process.

And While I am used to having a Process with 1 or 2 lines in it (as was described by PointWell when I originally wrote this) and then calling the ".1" or ".2" sub-process to do more Logic, one thing I've been wondering about is the Overhead of Aware writing Context as it spawns other Processes.
ANSWER: In simple tests, I have not seen it "write Context" except when the Process is FIRST started (from a Form button in my explanation below) - So there is no "overhead" of writing the current context IN A PROCESS before that PROCESS calls a subprocess (not that I have seen yet, I may be wrong). When we "pass" a BO by Specifying that BO as Input in a Called Process, Aware re-reads that Record anyway - its not passing current values in Memory.

???eh?

So If I passed in CUST as Input to this top level Process, and I follow your Logic (referring to PointsWell's main argument in that thread) to make a simple test, and then call Process_1 or Process_2, CUST gets also passed to those Processes.
---> I wonder if another internal Context record has to be written to disk to serve as input for those Processes - Aware could have no idea the complexity of the destination Process, and certainly couldn't know this is a tiny process only acting as a "sub-process" to save on Logic complexity. Of course, we'd need an answer from Support OR an analysis of SQL Profiler to see if we can see this context being written....

OK, 2 hours later I'm back with results.
It turns out that that I still really don't know whats in the binary code written into EXECUTION_CONTEXTS. But its not what I thought it was. (BTW, mine is 27,222 characters of ASCII when pasted into an Editor, so its half that in Bytes being written & read.) It probably is some Record IDs (Like the Main BO needed for input to the SubProcess - but its NOT actually the data thats in that Main BO record. We think, from other programming languages, that when we say we "pass" the Cust record to the Process we are passing values IN MEMORY (Sure, it can be call by name, call by reference, call by value - skip that for now) - But thats not what Aware is doing. (I assume this, because if it was the data of a record(s), then why re-read the Main BO from the DB, why not just unpack it from the binary?)

From the trace below, I can see this:
1) Whenever I'm on a Form and I click a Panel Operation to start a Process to send this Customer an Email, for example, Aware 1st re-reads that record (SELECT *) and reads ALL "ps" and "ob" reference tables (and a few more to resolve shortcuts).
*** Just because you "see it" on the form and you think its "current"... its NOT.
2) It then Writes a context record - but it appears it doesn't need to sometimes... it sniffs ahead (somehow) and IF I removed the Display Message action, then it doesn't write context (I only tested this about 10 times cause I couldn't figure out why test2 wasn't Writing/Reading EXECUTION CONTEXTS - I could be wrong, but thats what 30 mins of testing and hair pulling determined.) .
3) The 1st thing the subtask does: Read the CONTEXT record (but not every time, see #2 above)
4) Then it reads the MAIN BO (Select *)
5) Then it [again] reads all "ps" and "ob" reference tables (and a few more to resolve shortcuts).
--- This is where Test1 ends
6) Test2 (identical to test1 up to this point) calls Test2B (Following PointsWell's example/suggestion in this thread) and guess what???
7) The called Process: Read the CONTEXT record, Read the MAIN BO (Select *), Then it [again] ...... Get the idea?

So this begs the question...
DO I REALLY NEED TO WORRY IF AWARE DOES 4 COMPARISONS or 12 - which are in Memory,
OR Do I want the Rules Engine to work a little easier while, IN MY CASE, I'm going to do 9-15 more database reads by calling a SubProcess ?

(9-15?.... the Main BO, the 8 reference tables, plus various shortcuts)


A Simple Process Example:
Test1 - does nothing, only a Display Message
Test2 (and subtask Test2b) - Test2 just calls Test2b (the "sub-process", which also has BO as input), then Display Message

1 Button on a Form of the "Main BO". Button is Start Process. The "Main BO" is input of the Processes
Screen Shot 2020-01-30 at 10.06.21 PM.png
Screen Shot 2020-01-30 at 10.06.21 PM.png (31.62 KiB) Viewed 2627 times



Profiler Results:
--> My "Main BO" in these results is called RO (for Repair Order). I've omitted all the reference table reads. Sorry, this is hard to read. All my notes are in <brackets>. Most All else is direct from MSSQL Profiler.

TEST1 MSSQL Profiler

Code: Select all

<I am on a Form>
<I press a Panel Operation calling Test1, with RO as Input>
<it appears the server reads the record thats needed by called Process (ie. the Record on the Form)>
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM RO WHERE [email protected]        ',12914 <-- this is the record ID
   <8 more reference tables are read>
<save context>:
   exec sp_prepexec @p1 output,N'@P0 bigint,@P1 varbinary(8000),@P2 varbinary(max),@P3 bigint,@P4 nvarchar(4000),@P5 bit',N'INSERT INTO EXECUTION_CONTEXTS VALUES(@P0,@P1,@P2,@P3,@P4,@P5)',8274,NULL,0x789CED7D07981C47957FCF748FB4DA55B6259B60DC5E39DBDA24C996A   <snip>
<calls Process>
------- Now the Process starts -----------
<Test1 starts by reading Context passed to it>
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM EXECUTION_CONTEXTS WHERE [email protected]        ',8274  <-- record ID
<then it re-reads the main RO record that was passed to it>
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM RO WHERE [email protected]        ',12914
<8 more reference tables are read>
   exec sp_prepexec @p1 output,N'@P0 bigint',N'DELETE FROM EXECUTION_CONTEXTS WHERE [email protected]        ',8274
<nothing actually happens in this Process except DISPLAY MSG>
<end Test1>
-------- Leaves Process -----------
<it appears the main form reads Context back in>
<and main form refreshes - by reading main rec and the 8 reference tables>   
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM EXECUTION_CONTEXTS WHERE [email protected]        ',8274
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM RO WHERE [email protected]        ',12914

Read the main BO only 1 time actually in the process

TEST2 MSSQL Profiler

Code: Select all

read & save
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM RO WHERE [email protected]        ',12829
   exec sp_prepexec @p1 output,N'@P0 bigint,@P1 varbinary(8000),@P2 varbinary(max),@P3 bigint,@P4 nvarchar(4000),@P5 bit',N'INSERT INTO EXECUTION_CONTEXTS VALUES(@P0,@P1,@P2,@P3,@P4,@P5)',8546,NULL,0x789CED5D09981C4775EE99E991565A1D2BC9960DC4B8BDF2211FDA4B8 ... <snip>
---- process starts -----
read context, BO & reference tables
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM EXECUTION_CONTEXTS WHERE [email protected]        ',8546
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM RO WHERE [email protected]        ',12829
now a funky sequence where it Deletes context - then re-reads it (which doesn't make sense to me)
   exec sp_prepexec @p1 output,N'@P0 bigint',N'DELETE FROM EXECUTION_CONTEXTS WHERE [email protected]        ',8546
----- So I guess the SubProcess starts here -----
and repeats the read context, BO & reference tables
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM EXECUTION_CONTEXTS WHERE [email protected]        ',8546
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM RO WHERE [email protected]        ',12829
----- process ends -----

Read the main BO 2 times, 1 in main process, 1 in sub-process
—> JaymerTip Context passing analysis
Last edited by Jaymer on Sat Nov 21, 2020 11:45 pm, edited 2 times in total.
Click Here to see a collection of my tips & hacks on this forum? Or search for "JaymerTip" in the search bar at the top.

Jaymer
Aware Programming & Consulting - Tampa FL
Jaymer
Posts: 1889
Joined: Tue Jan 13, 2015 10:58 am
Location: Tampa, FL
Contact:

Large BOs

Post by Jaymer »

So what we can see from the above Profiler logs is that Aware usually/always does a "SELECT *" on the BO passed into a Process.

--> That stmt. doesn't make literal sense when I think about a parent finding 10 records, and "passing" those 10 (the Context) to a Process.
If I spent more time analyzing that, Aware is somehow recording the 10 IDs of the records "passed" to the Process, and each time it starts a thread for 1 of the 10, its doing "Select *" for that specific ID

Since LIRU (RegularUser) is (currently) the main mechanism people use for temporary storage and passing of data to processes, its easy to add a bunch of data in there which will be transferred constantly by Aware when when you don't need it... due to the "Select *" thats happening when A Process starts.

In a recent project, I had a 5000 byte field in LIRU that was JSON data to send in REST. Knowing this now, if would behoove me to clear that field when done - cause the varchar field will be null and prolly won't send that 5k of data now a million times a day.

If I have 10-20 users plugging away on a Order Entry system all day, and I have a process ON NEW of every OrderLine, its quite possible that tons of data are transferred around a system unknowingly. And they complain about us Americans wasting water!!! You're wasting DATA like it grows on trees!
Click Here to see a collection of my tips & hacks on this forum? Or search for "JaymerTip" in the search bar at the top.

Jaymer
Aware Programming & Consulting - Tampa FL
hpl123
Posts: 2333
Joined: Fri Feb 01, 2013 1:13 pm
Location: Scandinavia

Re: ANALYSIS: Aware behind the scenes. Context. Passing BOs.

Post by hpl123 »

Jaymer wrote: Sun Feb 02, 2020 12:17 am TLDR; Complex rules in an Aware Process are hard for Aware to evaluate correctly,
So add more sub-Processes to simplify a "parent" Process,
At what overhead cost to start subprocesses?


(feel free to leave comments!)

This post came about due to discussions about Aware not being able to handle complex IF/THEN/ELSE Rules in a Process.
This post
and Mark Bailey here.
Basically, if the Rule will let you enter in 6 IF/THEN/ELSE blocks and it has an internal runtime error where it cannot produce the correct answer, then its a bug. And I found a way to re-write my rule[s] so that it did give the right answer, but at what (if any) performance penalty.

This entire post was my reply to a post here.
(which was offering a solution where we make MORE processes in Aware (we have to make plenty already, so I'm not a fan).

And it got me thinking about what goes on in Aware when we call Processes - I always wonder what [Negative] effect is calling a subProcess going have if I pass it 3 or 4 BOs - is it ok? Should I re-write or do something a different way? Should I even worry about it at all? Does it use more memory to pass several big BOs? etc etc


So I'm removing it from that post for discussion here - so there's an awareness of these internals for people to consider when designing your systems.
(explanation of this in the next post in this thread)

The Limitation in Aware of having only 1 IF/THEN/ELSE per Rule is a pain.
Example:
Desired

Code: Select all

Rule1:  IF State = FL THEN
..... bunch of stuff here
IF AccessLevel="Admin" THEN WriteFLORIDALog 
...is not allowed IN a SINGLE RULE.
OK, so this isn't the end of the world. Not worth abandoning Aware for. I'm sure we can AGREE to settle on Option1 or Option2, right?

You could write it in a Single Rule like this:
Option1

Code: Select all

IF State = FL and AccessLevel="Admin" THEN .... bunch of stuff here... WriteFLORIDALog
ELSE
IF State = FL THEN .... bunch of stuff here
Or, You have to break it into 2 rules in the Process:
Option2

Code: Select all

Rule1: IF State = FL and AccessLevel="Admin" THEN .... bunch of stuff here... WriteFLORIDALog
Rule2: IF State = FL and AccessLevel<>"Admin" THEN .... bunch of stuff here
1st Discussion:
One main issue I have with both options is that I have physically copied my complex code 4 times in this process, instead of 1.
the "bunch of stuff here" could be this, for example:

Code: Select all

FIND Contacts WHERE Contacts=TicketE.ps_Contact1
CREATE NP_Print_Header WITH NP_Print_Header.DeptID=RO.ps_Dept.ID
Calc_Contact_Print_Headers_Estimate

EXPORT DOCUMENT RO_Estimate TO FILE SystemSettings.DocumentPath+'RO'+RO.RONo+'\Est_'+TicketE.TicketNum+'_1.PDF'

CREATE OutgoingEmail_to_Multiple WITH OutgoingEmail_to_Multiple.pm_Recipients=TicketE.ps_Contact1,OutgoingEmail_to_Multiple.Type='E',OutgoingEmail_to_Multiple.State='Queue',OutgoingEmail_to_Multiple.ps_RO=RO,OutgoingEmail_to_Multiple.Message=UNDEFINED,OutgoingEmail_to_Multiple.Subject='Repair Order Estimate for RO # '+RO.RONo,OutgoingEmail_to_Multiple.Attachment1_Path=SystemSettings.DocumentPath+'RO'+RO.RONo+'\Est_'+TicketE.TicketNum+'_1.PDF',OutgoingEmail_to_Multiple.Link1=`/logonOp.aw?e=`+ENCRYPT_B64(`domain=AAA&userName=`+Contacts.LoginName+`&password=`+Contacts.Password_Clear_Temp+`&testingMode=false&firstCommand=startProcess2,Estimate_Approve_OR_Reject,TicketE,`+TicketE.ID+`,main`)
log2 Contacts.LoginName

IMPORT DOCUMENT OutgoingEmail_to_Multiple.Attachment1 FROM SystemSettings.DocumentPath+'RO'+RO.RONo+'\Est_'+TicketE.TicketNum+'_1.PDF'
And I've been using a simple "State = FL" - when really thats something more realistic like:
IF TicketE.ps_Contact1 IS DEFINED AND TicketE.ps_Corp.PORequiredYN='Yes' AND TicketE.sc_Status='NEW' and AccessLevel='Admin'
MODIFY this with a NOT(), an OR or 2 of these in Parens and it gets complex fast.

Point is, to do Option1 or Option2 (which we agreed are perfectly good alternatives, didn't we?) now requires me to copy this large block of code 4 times - which makes it VERY easy for me to screw something up - and hard to maintain because whatever I did in that code needs to be changed in 4 places when the boss wants a change.
--> No reason to argue that all that code would be taken into a subprocess - cause thats the purpose of this whole thread anyway. Just go with it.

2nd Discussion:
The alternative post (way up at the top) would have me restructure my Process to say:
IF STATE=FL then Process_1
ELSE
IF STATE=TX then Process_2

(of course, with my more realistic test like this:
IF AccessLevel='Admin' AND TicketE.ps_Contact1 IS DEFINED OR ( TicketE.ps_Corp.PORequired<>'Always' AND TicketE.sc_Status='NEW' )
it becomes more difficult to do this anyway.

Anyway, that what starts the more detailed analysis below where I examine whats happening "overhead-wise" when I call another process.


3rd Discussion:
Option2 isn't that bad, but if I have lots of those, then the absence of a BREAK function to stop further processing in this Process makes a LOT more Rules/Rule Expressions have to be unnecessarily evaluated (more below).

TLDR 2 - All of this because Aware can't handle a complex IF/THEN/ELSE rule.
The Desired way, if it worked, wouldn't have me duplicate ANY code.





... and the inability to "BREAK" out of a Rule doesn't help.
In my complex Process in this post, a BREAK would help once the TRUE "IF" branch was found, hopefully saving time by not having to execute further down the Process. When I simplified my Process to 3 separate Rules, the BREAK would exit and prevent further unnecessary tests once the condition was met. So It wouldn't always be all 3 rules executing. In fact, I'd put my "most common" condition first so the majority of the time Aware would never see the 2nd or 3rd Rule because the Break would [gracefully] end the Process.

And While I am used to having a Process with 1 or 2 lines in it (as was described by PointWell when I originally wrote this) and then calling the ".1" or ".2" sub-process to do more Logic, one thing I've been wondering about is the Overhead of Aware writing Context as it spawns other Processes.
ANSWER: In simple tests, I have not seen it "write Context" except when the Process is FIRST started (from a Form button in my explanation below) - So there is no "overhead" of writing the current context IN A PROCESS before that PROCESS calls a subprocess (not that I have seen yet, I may be wrong). When we "pass" a BO by Specifying that BO as Input in a Called Process, Aware re-reads that Record anyway - its not passing current values in Memory.

???eh?

So If I passed in CUST as Input to this top level Process, and I follow your Logic (referring to PointsWell's main argument in that thread) to make a simple test, and then call Process_1 or Process_2, CUST gets also passed to those Processes.
---> I wonder if another internal Context record has to be written to disk to serve as input for those Processes - Aware could have no idea the complexity of the destination Process, and certainly couldn't know this is a tiny process only acting as a "sub-process" to save on Logic complexity. Of course, we'd need an answer from Support OR an analysis of SQL Profiler to see if we can see this context being written....

OK, 2 hours later I'm back with results.
It turns out that that I still really don't know whats in the binary code written into EXECUTION_CONTEXTS. But its not what I thought it was. (BTW, mine is 27,222 characters of ASCII when pasted into an Editor, so its half that in Bytes being written & read.) It probably is some Record IDs (Like the Main BO needed for input to the SubProcess - but its NOT actually the data thats in that Main BO record. We think, from other programming languages, that when we say we "pass" the Cust record to the Process we are passing values IN MEMORY (Sure, it can be call by name, call by reference, call by value - skip that for now) - But thats not what Aware is doing. (I assume this, because if it was the data of a record(s), then why re-read the Main BO from the DB, why not just unpack it from the binary?)

From the trace below, I can see this:
1) Whenever I'm on a Form and I click a Panel Operation to start a Process to send this Customer an Email, for example, Aware 1st re-reads that record (SELECT *) and reads ALL "ps" and "ob" reference tables (and a few more to resolve shortcuts).
*** Just because you "see it" on the form and you think its "current"... its NOT.
2) It then Writes a context record - but it appears it doesn't need to sometimes... it sniffs ahead (somehow) and IF I removed the Display Message action, then it doesn't write context (I only tested this about 10 times cause I couldn't figure out why test2 wasn't Writing/Reading EXECUTION CONTEXTS - I could be wrong, but thats what 30 mins of testing and hair pulling determined.) .
3) The 1st thing the subtask does: Read the CONTEXT record (but not every time, see #2 above)
4) Then it reads the MAIN BO (Select *)
5) Then it [again] reads all "ps" and "ob" reference tables (and a few more to resolve shortcuts).
--- This is where Test1 ends
6) Test2 (identical to test1 up to this point) calls Test2B (Following PointsWell's example/suggestion in this thread) and guess what???
7) The called Process: Read the CONTEXT record, Read the MAIN BO (Select *), Then it [again] ...... Get the idea?

So this begs the question...
DO I REALLY NEED TO WORRY IF AWARE DOES 4 COMPARISONS or 12 - which are in Memory,
OR Do I want the Rules Engine to work a little easier while, IN MY CASE, I'm going to do 9-15 more database reads by calling a SubProcess ?

(9-15?.... the Main BO, the 8 reference tables, plus various shortcuts)


A Simple Process Example:
Test1 - does nothing, only a Display Message
Test2 (and subtask Test2b) - Test2 just calls Test2b (the "sub-process", which also has BO as input), then Display Message

1 Button on a Form of the "Main BO". Button is Start Process. The "Main BO" is input of the Processes
Screen Shot 2020-01-30 at 10.06.21 PM.png




Profiler Results:
--> My "Main BO" in these results is called RO (for Repair Order). I've omitted all the reference table reads. Sorry, this is hard to read. All my notes are in <brackets>. Most All else is direct from MSSQL Profiler.

TEST1 MSSQL Profiler

Code: Select all

<I am on a Form>
<I press a Panel Operation calling Test1, with RO as Input>
<it appears the server reads the record thats needed by called Process (ie. the Record on the Form)>
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM RO WHERE [email protected]        ',12914 <-- this is the record ID
   <8 more reference tables are read>
<save context>:
   exec sp_prepexec @p1 output,N'@P0 bigint,@P1 varbinary(8000),@P2 varbinary(max),@P3 bigint,@P4 nvarchar(4000),@P5 bit',N'INSERT INTO EXECUTION_CONTEXTS VALUES(@P0,@P1,@P2,@P3,@P4,@P5)',8274,NULL,0x789CED7D07981C47957FCF748FB4DA55B6259B60DC5E39DBDA24C996A   <snip>
<calls Process>
------- Now the Process starts -----------
<Test1 starts by reading Context passed to it>
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM EXECUTION_CONTEXTS WHERE [email protected]        ',8274  <-- record ID
<then it re-reads the main RO record that was passed to it>
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM RO WHERE [email protected]        ',12914
<8 more reference tables are read>
   exec sp_prepexec @p1 output,N'@P0 bigint',N'DELETE FROM EXECUTION_CONTEXTS WHERE [email protected]        ',8274
<nothing actually happens in this Process except DISPLAY MSG>
<end Test1>
-------- Leaves Process -----------
<it appears the main form reads Context back in>
<and main form refreshes - by reading main rec and the 8 reference tables>   
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM EXECUTION_CONTEXTS WHERE [email protected]        ',8274
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM RO WHERE [email protected]        ',12914

Read the main BO only 1 time actually in the process

TEST2 MSSQL Profiler

Code: Select all

read & save
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM RO WHERE [email protected]        ',12829
   exec sp_prepexec @p1 output,N'@P0 bigint,@P1 varbinary(8000),@P2 varbinary(max),@P3 bigint,@P4 nvarchar(4000),@P5 bit',N'INSERT INTO EXECUTION_CONTEXTS VALUES(@P0,@P1,@P2,@P3,@P4,@P5)',8546,NULL,0x789CED5D09981C4775EE99E991565A1D2BC9960DC4B8BDF2211FDA4B8 ... <snip>
---- process starts -----
read context, BO & reference tables
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM EXECUTION_CONTEXTS WHERE [email protected]        ',8546
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM RO WHERE [email protected]        ',12829
now a funky sequence where it Deletes context - then re-reads it (which doesn't make sense to me)
   exec sp_prepexec @p1 output,N'@P0 bigint',N'DELETE FROM EXECUTION_CONTEXTS WHERE [email protected]        ',8546
----- So I guess the SubProcess starts here -----
and repeats the read context, BO & reference tables
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM EXECUTION_CONTEXTS WHERE [email protected]        ',8546
   exec sp_prepexec @p1 output,N'@P0 bigint',N'SELECT * FROM RO WHERE [email protected]        ',12829
----- process ends -----

Read the main BO 2 times, 1 in main process, 1 in sub-process
—> JaymerTip Context passing analysis
Very interesting post and thanks for sharing the analysis. SO, what is the bulletpoint best practices, conclusions and current consensus on this? I found it a bit hard to extract the essence. Thanks
Henrik (V8 Developer Ed. - Windows)
nhofkes
Posts: 41
Joined: Mon Sep 07, 2020 6:03 am
Location: Netherlands

Re: ANALYSIS: Aware behind the scenes. Context. Passing BOs.

Post by nhofkes »

the inability to "BREAK" out of a Rule doesn't help. In my complex Process in this post, a BREAK would help once the TRUE "IF" branch was found, hopefully saving time by not having to execute further down the Process. When I simplified my Process to 3 separate Rules, the BREAK would exit and prevent further unnecessary tests once the condition was met. So It wouldn't always be all 3 rules executing. In fact, I'd put my "most common" condition first so the majority of the time Aware would never see the 2nd or 3rd Rule because the Break would [gracefully] end the Process.
This was written before version 8.5. I think that this BREAK functionality is now available in 8.5 with END PROCESS so that may help a bit.

Another thought on this: I am assuming that the SQL engine has some form a caching mechanism? I.e. although a separate SQL read me performed when a subprocess is called, it may be possible that the SQL engine provides the data out of the cache if it has read the same information shortly before and it may not be necessary to perfom a new actual disk IO. If that is the case, the penalty of the additional read may be less than what it appears on first sight of the SQL profiler output.
Niels
(V8.5 build 2828 - MariaDB - Windows)
hpl123
Posts: 2333
Joined: Fri Feb 01, 2013 1:13 pm
Location: Scandinavia

Re: ANALYSIS: Aware behind the scenes. Context. Passing BOs.

Post by hpl123 »

nhofkes wrote: Thu Dec 10, 2020 1:36 pm
the inability to "BREAK" out of a Rule doesn't help. In my complex Process in this post, a BREAK would help once the TRUE "IF" branch was found, hopefully saving time by not having to execute further down the Process. When I simplified my Process to 3 separate Rules, the BREAK would exit and prevent further unnecessary tests once the condition was met. So It wouldn't always be all 3 rules executing. In fact, I'd put my "most common" condition first so the majority of the time Aware would never see the 2nd or 3rd Rule because the Break would [gracefully] end the Process.
This was written before version 8.5. I think that this BREAK functionality is now available in 8.5 with END PROCESS so that may help a bit.

Another thought on this: I am assuming that the SQL engine has some form a caching mechanism? I.e. although a separate SQL read me performed when a subprocess is called, it may be possible that the SQL engine provides the data out of the cache if it has read the same information shortly before and it may not be necessary to perfom a new actual disk IO. If that is the case, the penalty of the additional read may be less than what it appears on first sight of the SQL profiler output.
Good point and it would be strange if it had no caching mechanism.
Henrik (V8 Developer Ed. - Windows)
PointsWell
Posts: 1067
Joined: Tue Jan 24, 2017 5:51 am
Location: 'Stralya

Re: ANALYSIS: Aware behind the scenes. Context. Passing BOs.

Post by PointsWell »

nhofkes wrote: Thu Dec 10, 2020 1:36 pm
the inability to "BREAK" out of a Rule doesn't help. In my complex Process in this post, a BREAK would help once the TRUE "IF" branch was found, hopefully saving time by not having to execute further down the Process. When I simplified my Process to 3 separate Rules, the BREAK would exit and prevent further unnecessary tests once the condition was met. So It wouldn't always be all 3 rules executing. In fact, I'd put my "most common" condition first so the majority of the time Aware would never see the 2nd or 3rd Rule because the Break would [gracefully] end the Process.
This was written before version 8.5. I think that this BREAK functionality is now available in 8.5 with END PROCESS so that may help a bit.

Another thought on this: I am assuming that the SQL engine has some form a caching mechanism? I.e. although a separate SQL read me performed when a subprocess is called, it may be possible that the SQL engine provides the data out of the cache if it has read the same information shortly before and it may not be necessary to perfom a new actual disk IO. If that is the case, the penalty of the additional read may be less than what it appears on first sight of the SQL profiler output.
My opinion is that if you have to use the PROCESS END then there is an issue with your decision tree and your process architecture which is the discussion which prompted this analysis by Jaymer.

If your process consists of long and wide decision trees with redundant conditions in that tree

Eg
If Status ='Open' and City=''New York' THEN ... ELSE
If Status ='Open' and City='Chicago' THEN ...

This is actually more modularly dealt with by
If Status='Open' THEN CallOpenSubprocess

Where CallOpenSubProcess is
If City='New York' THEN ... ELSE
If City='Chicago' THEN

Your code is more redundant in the first example than the second and you are making AIM do all of the lifting. If you look at how these redundant rules are being evaluated it is quite complex

Efficient code reuse can be applied for example if there are steps common to New York sub process and Chicago sub process then your code can be even more modularly broken down to call a common sub process

Eg
If City='New York' THEN [DoNewYorkSteps] CallInvoicingSubProcess ELSE
If City='Chicago' THEN [DoChicagoSteps] CallInvoicingSubProcess

Where CallInvoicingSubProcess carries out some common activity.

The process to sub process hierarchy does not need to be like a family tree constantly fanning out in a divergent pattern as it devolves, it can be divergent then convergent when calling sub processes

There is a whole branch of Quantitative Analytics devoted to decision tree optimisation and the number of steps required to evaluate an answer from a complex set of decisions (in much the same way that SQL queries have efficient structures versus inefficient).

What the analysis does not establish is whether
SQL calls being designed for data retrieval of data sets is more or less efficient than
AIM as a rule processor faced with a highly redundant and highly complex rule.

Further analysis is required to determine AIM decision load versus SQL query load when coupled with data being off loaded after not passing AIM rule tests.

For example
Is the load of a closed order for Chicago in the example above dropped from the hypothetical example above, sooner or later in each method.

Personally coming from an old school IBM background off loading to SQL was preferable to managing data in program. The chances are that your code was not as efficient as the accumulated output of many hundreds of thousands of hours that had been invested in the optimisation of the database engine. Vlad is a hero in terms of what AIM does but I am pretty sure that the immense effort he's put into AIM is a drop in the ocean compared to the amount of optimisation that has been applied to SQL.
hpl123
Posts: 2333
Joined: Fri Feb 01, 2013 1:13 pm
Location: Scandinavia

Re: ANALYSIS: Aware behind the scenes. Context. Passing BOs.

Post by hpl123 »

Aware´s rule engine is based on the Rete algorithm: https://en.wikipedia.org/wiki/Rete_algorithm and that is what processes the decision tree etc. and that part of it I think is difficult to optimize or do better. My understanding based on Vlad´s various comments is that the rule engine decision tree (rule evaluations) activity etc. carries a very low load, what taxes the system are the actions and operations (e.g do this, change that, find) executed based on successful rule execution / result.

What I am interested in knowing is basically how to optimize our apps to minimize Aware actions and database activity / transactions etc. (how many times something is loaded into context and things like that, are things executed / loaded unnecessarily e.g multiple times due to how we set up rules VS processes and when we use SystemSettings or LIRU etc. etc.) and also of course, what we can / should do and in what scenarios to improve speed / minimize load etc. in our apps e.g when to use a stored procedure because Aware does x slow etc. etc..
Henrik (V8 Developer Ed. - Windows)
PointsWell
Posts: 1067
Joined: Tue Jan 24, 2017 5:51 am
Location: 'Stralya

Re: ANALYSIS: Aware behind the scenes. Context. Passing BOs.

Post by PointsWell »

hpl123 wrote: Sat Dec 12, 2020 10:52 am Aware´s rule engine is based on the Rete algorithm: https://en.wikipedia.org/wiki/Rete_algorithm and that is what processes the decision tree etc. and that part of it I think is difficult to optimize or do better. My understanding based on Vlad´s various comments is that the rule engine decision tree (rule evaluations) activity etc. carries a very low load, what taxes the system are the actions and operations (e.g do this, change that, find) executed based on successful rule execution / result.

What I am interested in knowing is basically how to optimize our apps to minimize Aware actions and database activity / transactions etc. (how many times something is loaded into context and things like that, are things executed / loaded unnecessarily e.g multiple times due to how we set up rules VS processes and when we use SystemSettings or LIRU etc. etc.) and also of course, what we can / should do and in what scenarios to improve speed / minimize load etc. in our apps e.g when to use a stored procedure because Aware does x slow etc. etc..
The engine's efficiency can be degraded quite easily by adding overly ornate and complex conditions into it. As you watch the log it is calculating the rule and the inverse of the rule.

If you process IF Case = Open the rule engine also checks IF Case <> Open. The more complicated the condition creates an inverse that is more complex, so the rule processing becomes more complicated by considering the permutations of the inverse. While I agree that there is little you can do to make the rules engine more efficient, there is plenty that you can do to make it less efficient. Processing simple conditions but more of them is more efficient than checking one very complex condition with all the options fed in at once.

My point is that if you have multiple simple rules but do more SQL are you creating a greater overall overhead than if you create massive complex conditions for the rules engine but a simple SQL query.

If R=Result, A=Aim Effort and S=SQL Effort which is more efficient R=2A+S or R=A+2S? I am not sure that this analysis determines that.

What I do know is that whenever Vlad has assisted me and he's been keener to utilise as simple conditions as possible.
Post Reply