I'm working on a POC to see if we can migrate from ODM to Drools, but I'm not able to identify certain features within Drools that we utilize in our ODM apps.
Example:
- In ODM we utilize a Decision Operation that has inputs and outputs, which is invoked from a Spring app. This ODM operation invokes a complex ruleflow (parent/sub flows), which organizes rule execution based on different levels of metadata. The rules, if condition is met, builds a set of output data that the Spring app uses to behave differently based on the result. Keep in mind the Decision Operation waits for the ruleflow to complete before responding back to the spring app with that output.
In Drools, I understand I can build a spring boot app and invoke a rule flow, but the rule flow is then async. Drools doesn't behave the same as ODM so doesn't appear I can use a rule flow. There are options to wait and get the state of the rule flow, but we receive millions of requests a day and can't justify putting in waits. I thought about executing rules utilizing agenda-group/activation-group to control our execution, but the data we are evaluating is complex, lots of cross data-validating, and the data we are validating (POJOs) can be 10 levels deep. Yes, our structure is massive and there can be up to 500+ rules. I just can't see utilizing agenda-group/activation-group to achieve what we need due to our complexity.
Would anyone have any thoughts on possibly achieving this type of behavior? Any input is appreciated :)
I don't know anything about ODM, so I can't comment about the similarities/differences. But from what is described in the question about the use case, I think the problem is that you're using Drools wrong. :)
Generally speaking, we'd invoke rules like this:
fireAllRules
is a synchronous, blocking method.Alternatively, if you want to do batch processing, you'd do something like this (example taken from the documentation):
Notice we call
startProcess
in the list of commands, but not directly. Further we still wait for the results at theexecute
call, so whilestartProcess
does proceed asynchronously, we still block for all executions to complete before continuing atexecute
.Your stated use case was:
There's nothing here that precludes you from using Drools. At my previous company, we had a set of rules that actually routed requests between services based on the state of the data in the request, including modifying the request in flight. We also had rules that could do normal processing like validation, calculation, and so on.
Your model depth is also not really a hindrance -- you can write rules against your model no matter how it's structured. There are some types which are inherently inferior (please don't pass
Map
s into the rules) but generally if it's in working memory you can work with it.Finally, processing time is a factor mostly of how well written your rules are. There are structures in Drools that allow you to modify working memory and rerun the rules -- this will cause them to take inherently more time because (duh) you're running the rules a second time! But a simple fall-through sort of rules -- trigger which ever ones trigger and keep moving -- those can be very fast. At my last job I had a single service with 1.4 million rules that had a sub-second SLA; it was so fast because there were no 'update' calls, and none of the rules cared about which of the other rules also fired. The rules were effectively stateless, and the worst-case performance was 300 milliseconds (average was 30 ms; and that's round trip through the entire spring-boot app and not just the actual rule evaluation.)
Parsing out the example, we have these models (assume getters and setters on all fields):
To get to the 'options', we just pull them out of the intermediate models. Assuming you put a
Person
instance into working memory:Drools is generally pretty good at doing defensive 'gets', but you can be defensive in your own right by adding a null check. For example:
Alternatively if you want to fail fast and just discard any inputs with bad data, you can
retract
them from working memory. This is potentially dangerous because it's a premature exit from the workflow, and can cause potential bugs downstream if you don't realize it's there. (It's somewhat analogous to having multiple "return" statements in your Java code; if you're debugging down near the bottom of a method and aren't aware that there's an early return much farther up, that might cause you to waste time or introduce unexpected behavior.)Word of caution: once you call
retract
, that item is gone from working memory, so if any subsequent rules were relying on it, they'll no longer fire.The salience isn't necessary but historically I've found it easier to mentally track early retractions like this when they're explicitly called out as happening earlier than the regular rules. Generally speaking it's better practice to simply write rules that are mutually exclusive, but you may find marginally better performance using an early retract.