I am using Drools Rule Engine for my usecase where i have to determine owner of a product based on multiple attributes. I have created a set of rules.
I have around 1000 input products and about 50 rules. I am using drool rule engine to map the owner for each product using the 50 rules. All the 50 rules have to be applied for each product to determine the owner.
Here is an example of my rule
rule "create drool rule from input values with rule id - rule_1"
salience 110
activation-group "product-owner-assignment-rules-group"
when
$product: Product()
Product(productName == "Jigsaw")
Product(productType == "Resistor")
Product(productManufacturerName == "Corp.Inc")
then
$product.setOwnerName("Mr.X");
$product.setRuleId("rule_1");
end
My Drools rule executor looks like this
public void executeRules(@NonNull final List<Rule> droolRules, @NonNull final List<Product> productOwners) {
final KnowledgeBuilder knowledgeBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
for (final String rule : droolRules) {
knowledgeBuilder.add(ResourceFactory.newByteArrayResource(rule.getBytes(Charset.forName(StandardCharsets.UTF_8.name()))),
ResourceType.DRL);
}
if (knowledgeBuilder.hasErrors()) {
String errorMessage = "Encountered errors while building rules into the Knowledge Builder:\n" + knowledgeBuilder.getErrors();
log.error(errorMessage);
throw new RuntimeException(errorMessage);
}
final InternalKnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase();
knowledgeBase.addPackages(knowledgeBuilder.getKnowledgePackages());
final StatelessKieSession kieSession = knowledgeBase.newStatelessKieSession();
productOwners.forEach(productOwnerValue -> {
kieSession.execute(productOwnerValue);
});
}
This is working perfectly fine if i rule the StatelessKieSession per Product object. But is causing performance impact as it is looping through each product.
If i want to execute all the facts and rules together at once, the rule engine is only updating the last fact with the RHS of first rule match.
final KieSession kieSession1 = knowledgeBase.newKieSession();
productOwners.forEach(productOwnerValue -> {
kieSession.insert(productOwnerValue);
});
kieSession1.fireAllRules();
Can anyone help me understand how can i fire all the rules with all facts at once to speed up the performance and also have the correct results.
Your rule is not doing what you think it does.
You want to find a single product with name "Jigsaw", type "Resistor", and manufacturer "Corp.Inc". Then you want to update that single product with owner name and rule id.
What your rule is actually doing is checking that there exists a product with name Jigsaw; a product with type Resistor; and a product with manufacturer Corp.Inc. Not one product with all 3 properties (though it may find one that meets that condition, it's not a requirement the way the rule is defined.). Also it's assigning owner name and rule id to a random product in working memory, not necessarily any of the ones that match the properties that you've checked for.
This is what your rule should look like instead:
Every statement in the "when" clause is evaluated individually. So what your rule is doing is this:
First, identify a product in working memory and assign it the variable
$product
. There are no conditions specified here, so it will match any product present in memory.Look for a Product in working memory with product name "Jigsaw". Since the result of this call isn't assigned to a variable, it's equivalent to just checking for the existence of a product with this name.
Similarly, look for a Product with type "Resistor". This is also an existence check. There's no guarantee that what it finds will be the same one identified as having the name "Jigsaw", nor necessarily the one assigned to the variable
$product
.Find a product with the manufacturer "Corp.Inc". Also an existence check and no relationship required with the other statements as mentioned earlier.
What does that mean practically? Consider these items:
None of these inputs meet all of your conditions, but your rule as written would still trigger because there is at least one Product that meets each condition individually. Which one would be
$product
depends on a lot of things including order and how these objects serialize. (Further it's entirely likely that this rule would trigger multiple times since$product
isn't restricted at all.)The reason it "works" when you run with only one product in working memory is because all of the conditions happen to be satisfied on the one item in working memory. Only when you start scaling up to multiple items does it become clear that the rule doesn't actually work as intended.