I am working with Drools 5.6.0 and I’m ready to upgrade to 6.0 so this issue is relevant for both versions.
I have googled a lot about using Drools in a multithreaded environment and I am still unsure how to proceed. In the following scenario I’m trying to find a way to use a singleton StatefulKnowledgeSession pre-initialized with a large number of static facts as business logic for a web service.
I would like to know if there is a best practice for the scenario further described below.
I create a StatefulKnowlegdeSession singleton when the server starts
Right at the initialization I insert over 100.000 Facts into the StatefulKnowlegdeSession. I call these „static facts“ since they will not ever be modified by the rules. Static facts act more like a set of big lookup tables.
Now the rule engine is placed into a web service (Tomcat). The web service receives a Request object which will be inserted into the KnowledgeSession. After fireAllRules() I expect the KnowledgeSession to calculate an output object which is to be returned as web service Response.
The calculation of the Response makes use of the static facts. The rules create a lot of temporary objects which are inserted into the working memory using insertLogical(). This makes sure that all garbage will be removed from working memory as soon as I retract the original Request object at the end of the web service call.
Now the question is how I will make this work in a multithreaded server?
As far as possible I would like to use only one StatefulKnowledgeSession instance (a singleton) because the static facts are BIG and it could become a memory issue.
I cannot use StatelessKnowledgeSessions freshly created at the beginning of each web service call because inserting all the static facts would take too long.
I am aware of the fact that StatefulKnowlegdeSession is not thread safe. Also, the partitioning option is not supported any more.
However, different WorkingMemoryEntryPoints / EntryPoints can be used from different threads. I could use a pool of EntryPoints and each web service call would use one instance from the pool for inserting the web service request.
This also means that I would need to multiply my rules (?) each using one particular EntryPoint, or at least the first rule, matching web service Request objects:
rule “entry rule for WORKER-1” // rule to be duplicated for entry points WORKER-2, WORKER-3,...
when
$req : Request () from entry-point “WORKER-1”
$stat : StaticFact( attr = $req.getAttr() )
then
insertLogical( new SomeTemporaryStuff ( $req ) );
end
rule “subsequent rule”
when
$tmp : SomeTemporaryStuff()
then
...go on with the calculation and create a Response at some point...
end
Subsequent rules create temporary objects in the working memory, and at this point I’m really afraid of messing up something if I’d be bombing the engine with dozens of concurrent Requests.
I would not use multiple entry points. Queue the requests to the thread running the session. If you want to utilize a multicore, run several services or service threads.
For your 100k facts, check carefully how its fields are represented. It's possible that String.intern() can provide considerable savings. Other objects can - since it is all static - be shared. Typically, in this sort of scenario, some extra overhead during element construction is beneficial later on, e.g., less GC overhead.
(Otherwise this is a very nice summary, almost a "howto" for runnning this scenario. +1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With