Note: A better question is written about this topic. Please vote for this one to be closed.
I ran into a weird case which i'm trying to understand why it happens. The issue is where a rule is constantly triggering it's consequence for it's last fact, without anything related to it being changed. I should note that i am using drools 7.0.0
Behavior analysis
I have the following rules:
rule "TicketsBoughtPerClass"
when
$ticketClass : TicketClass($customer : customer)
accumulate(
Ticket
(
customer != null,
customer == $customer,
ticketPrice >= $ticketClass.startPriceRange,
ticketPrice <= $ticketClass.endPriceRange
);
$ticketCount : sum(1)
)
then
System.out.println("Total " + $ticketCount + " bought tickets for " + $ticketClass.getClassName());
insertLogical(new TotalTicketsBoughtForClass($ticketClass, $ticketCount));
end
rule "TicketsNeededForBonus"
when
$ticketClass : TicketClass($minTicketsNeededForBonus : minTicketsNeededForBonus)
TotalTicketsBoughtForClass(ticketClass == $ticketClass, ticketCount < $minTicketsNeededForBonus, $ticketCount : ticketCount)
then
//Do something based on ($minTicketsNeededForBonus - $ticketCount)
end
The idea is to count the number of Ticket objects that are within a price range of a TicketClass for a Customer. However, as i mentioned, regardless of the price of the Ticket, there is always a rule trigger for the last inserted fact.
I added <- no match for the triggers of interest.
Here is a sample output:
Ticket classes (inserting ticketClass facts):
First class - Start price range: 200, End price range: 300
Second class - Start price range: 100, End price range: 199
Third class - Start price range: 50, End price range: 99
Buying tickets:
Bought ticket #0 for 60$ (insert)
Bought ticket #1 for 199$ (insert)
Bought ticket #2 for 250$ (insert)
Calling initial fireAllRules()
Total 1 bought tickets for Third class
Total 1 bought tickets for Second class
Total 1 bought tickets for First class
Changed ticket #0 from 60$ to 168$ (update)
fireAllRules() called
Total 0 bought tickets for Third class
Total 2 bought tickets for Second class
Changed ticket #0 from 168$ to 233$ (update)
fireAllRules() called
Total 0 bought tickets for Third class <- no match
Total 1 bought tickets for Second class
Total 2 bought tickets for First class
Changed ticket #0 from 233$ to 230$ (update)
fireAllRules() called
Total 0 bought tickets for Third class <- no match
Total 2 bought tickets for First class
Changed ticket #0 from 230$ to 283$ (update)
fireAllRules() called
Total 0 bought tickets for Third class <- no match
Total 2 bought tickets for First class
Changed ticket #0 from 283$ to 167$ (update)
fireAllRules() called
Total 0 bought tickets for Third class <- no match
Total 2 bought tickets for Second class
Total 1 bought tickets for First class
Changed ticket #0 from 167$ to 24$ (update)
fireAllRules() called
Total 0 bought tickets for Third class <- no match
Total 1 bought tickets for Second class
Changed ticket #0 from 24$ to 1$ (update)
fireAllRules() called
Total 0 bought tickets for Third class <- no match
Changed ticket #0 from 1$ to 0$ (update)
fireAllRules() called
Total 0 bought tickets for Third class <- no match
Changed ticket #0 from 0$ to 8$ (update)
fireAllRules() called
Total 0 bought tickets for Third class <- no match
Changed ticket #0 from 8$ to 40$ (update)
fireAllRules() called
Total 0 bought tickets for Third class <- no match
Unless there is something i have misunderstood, this should not be the case.
Debugging
I decided to dig in a little deeper into the drools core to get some answers myself. The search led me to the evaluation of the updated tuples in the accumulate node in PhreakAccumulateNode.doRightUpdates, which is where this additional trigger seems to be coming from. Just before the method doRightUpdatesProcessChildren, the following code is present:
// if LeftTupleMemory is empty, there are no matches to modify
if ( leftTuple != null ) {
if ( leftTuple.getStagedType() == LeftTuple.NONE ) {
trgLeftTuples.addUpdate( leftTuple ); //<----
}
doRightUpdatesProcessChildren( ARGS );
}
In short, this code always adds the first left tuple that is in AccmulateMemory as being updated, even though it isn't, which is what causes the RuleTerminalNode to always fire it's consequence for this tuple.
My current dilemma is that I don't understand if this call for trgLeftTuples.addUpdate( leftTuple ) is done on purpose, because when I took a look at the code in PhreakJoinNode, extremely similar code is present when processing the updated tuples, but this call is missing before doRightUpdatesProcessChildren is called.
The main question i have about this if it is the expected behavior, and if so, why?.