Unexpected global value set through rules

312 views
Skip to first unread message

Pragati Joshi

unread,
Sep 6, 2023, 1:34:59 PM9/6/23
to Drools Setup

I am working on a Java application that uses the Drools rule engine to evaluate multiple DRL files, each containing different rules. Each rule is supposed to add a value to a global List if certain conditions are met.

I have two DRL files, Apple.drl and Mango.drl, both sharing a global Collection called myBasketList. Here are the rules defined in these files:

Apple.drl

global FruitUtility fruitUtility; 
global Collection myBasketList; 
 rule "AppleRule"
salience 10 
when $basket : Basket() 
 eval(fruitUtility.isApple($basket.fruit)) 
then 
 myBasketList.add("Apple"); 
end

Mango.drl

global FruitUtility fruitUtility; 
global Collection myBasketList;
 rule "MangoRule"
salience 10
when $basket : Basket()
 eval(fruitUtility.isMango($basket.fruit)) 
then myBasketList.add("Mango");
 end

The code responsible for executing these rules creates a KieSession, sets the global variable, and fires the rules based on certain conditions. Here's how it looks:

@Override public Collection<String> executeRules(RuleParameters ruleParameters) { Collection<String> myBasketList = new ArrayList<String>(); 
final Map<String, Object> ruleGlobals = ImmutableMap.<String, Object>builder() .put("fruitUtility", fruitUtility)
 .put("myBasketList", myBasketList) 
 .build();

//some code to get the rulesProvider 

 droolsExecutor.executeRules(rulesProvider, ruleParameters.getFruit(), ruleGlobals); return myBasketList; 
 }

This is the rulesProvider

public interface RulesProvider {
 List<Resource> getRules();
 String getKieSessionName()
 String getKieBaseName()
}

@Getter
@AllArgsConstructor 
public class Basket
private String fruit; 
public class FruitUtility
public boolean isApple(String input){
if(input == "Apple"){ 
return true
 } 
return false
 } 

public boolean isMango(String input)
if(input == "Mango")
{ return true
 }
return false
 } }

I am creating a closeableKieSession and setting the global variable.

public void executeRules(@NonNull final RulesProvider rulesProvider, @NonNull final Object fact, Map<String, Object> globals) throws RuleEvaluationException {
    final KieContainer kieContainer = droolsKieContainerProvider.getKieContainer(rulesProvider);

    /**
     * A new session creation is required each time, because KieSession.fireAllRules is not thread safe.
     * Only a single thread enters fireAllRules at a time.
     * If a new session is not created every time, the rules will become a blocker for processing multiple
     * messages in parallel.
     * KieContainer creation is a heavy operation, and this KieContainer is a singleton.
     */
    final KieSession session = kieContainer.newKieSession();

    try (CloseableKieSession closeableSession = new CloseableKieSession(session)) {
        final KieSession kieSession = closeableSession.getSession();

        if (globals != null) {
            globals.forEach((key, val) -> {
                if (val == null) {
                    throw new IllegalArgumentException("Null value passed for global variable: " + key);
                }
                kieSession.setGlobal(key, val);
            });
        }

        setupFactAndFireAllRules(fact, kieSession);

    } catch (DependentServiceFailureException exception) {
        throw exception;
    } catch (RuntimeException exception) {
        throw new RuleEvaluationException(String.format(EXCEPTION_WHEN_EVAL_FACT, fact), exception);
    }
}


The executeRules method uses a RulesProvider to obtain the DRL rules, and a KieSession is created and managed with a custom CloseableKieSession.

Now, here's the issue: In one instance over two months of running this code, the behaviour was inconsistent. The fruit string passed to the rule was "Apple". Instead of "Apple" being inserted into myBasketList (as expected), "Mango" was inserted. Furthermore, it's worth noting that the "MangoRule" was executed before the "AppleRule".

I suspect that there might be a problem with the thread safety of the KieSession, but I cannot reproduce this issue, as in my testing, only "Apple" is inserted into the list as expected.

Does anyone have insights into why this inconsistency might occur? Are there any potential thread safety issues with the way KieSession is being used in this scenario? Any guidance or debugging tips would be greatly appreciated.

Reply all
Reply to author
Forward
0 new messages