0
votes

I'm working on writing a rule set in Drools and the following situation causes an infinite loop by retriggering the rule:

rule "My Rule"
    when
        a1:ObjectA()
        b1:ObjectB(field1 > 0 || a1.field1 in (1,2,3))
        ObjectB(field2 > 10)
    then
        modify( b1 ) { setField3(5) };
end

The following rule change doesn't result in an infinite loop, i.e., when a1 is no longer referenced within ObjectB:

rule "My Rule"
    when
        b1:ObjectB(field1 > 0 || field4 in (1,2,3))
        ObjectB(field2 > 10)
    then
        modify( b1 ) { setField3(5) };
end

Another scenario which doesn't cause an infinite loop is when I change the || to an && in the second when line:

rule "My Rule"
    when
        a1:ObjectA()
        b1:ObjectB(field1 > 0 && a1.field1 in (1,2,3))
        ObjectB(field2 > 10)
    then
        modify( b1 ) { setField3(5) };
end

From the Drools docs I understand that calling modify(){} will "trigger a revaluation of all patterns of the matching object type in the knowledge base," but since the field I'm modifying, field3, isn't used in the LHS conditions, I didn't think it should reevaluate. However, I am faily certain it has to do with referencing a1.field1 within ObjectB, but I can't find a concrete reason why. Thanks in advance!

1

1 Answers

1
votes

It matters at the object level, not the field level. Since you modify a1, the rule is re-evaluated because it relies on the ObjectA objects in working memory. Note that the docs indicate it will "trigger a reevaluation of all patterns of the matching object type in the knowledge base." Not the parameter values.

One way you could avoid this would be to add a constraint like field3 != 5 on the left hand side. That is:

rule "My Rule"
when
  a1: ObjectA( field3 != 5 ) // <-- if field3 is already 5, don't fire the rule
  b1: ObjectB(field1 > 0 || a1.field1 in (1,2,3))
  exists(ObjectA(field2 > 10))
then
  modify( a1 ) { setField3(5) };
end

Basically you need to make the rule no longer eligible for re-fire.

Alternatively, depending on your structure you could try to use no-loop, but that only keeps the rule from refiring when immediately triggered by the right hand side. If you have multiple modifications/updates/etc going on, you could get into a "broader" looping scenario where multiple rules cause reevaluation. (Example: Rule A does a re-evaluate, no-loop keeps A from firing again; then B causes re-eval, so A can trigger because no-loop does not apply.)