5
votes

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.

  1. I create a StatefulKnowlegdeSession singleton when the server starts

  2. 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.

  3. 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.

  4. 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 could also start the KnowledgeSession in “fireUntilHalt” mode but in this case I don’t know how I could get a synchronous response from the rule engine for returning it as web service Response.
3

3 Answers

1
votes

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

0
votes

For what sounds like a similar system, I just use 'synchronized' on the service method. The service being a Spring bean. There aren't loads of users and responses are fast, so queuing is rare and minimal.

Depending on the number of concurrent clients which might be invoking the service and how long each request takes to get a response, you could also create a small pool of services (memory permitting).

0
votes

I know this is somewhat old...but I'm posting an answer hoping the information helps somebody in a similar situation using Drools 6.x.

Two things I've learned about Drools over the past few days:

  • I've been caching creating of KnowledgeBase because i create the DRL objects at runtime, and once created, i cache it (using Google Guava)...but I've learned that creating StatefulKnowledgeSession (using newStatefulKnowledgeSession()fast (enough) in a single-threaded environment...but once you go multi-threaded (where I create a newStatefulKnowledgeSession` per request), you can see that creation takes longer and longer (as if new session creation tasks are being serialized) as confirmed (sort of) in this nabble forum thread
  • Knowledge* classes have been deprecated by newer Kie* classes in version 6.0 (which is annoying, since 99% of examples still use the older classes) ... so KnowledgeBase is replaced by KieContainer and StatefulKnowledgeSession is replaced by KieSession...in my process of optimizing my code, I upgraded from the 5.x classes to the 6.x classes

In my case (I use Drools 6.x in a REST service), I ended up pooling the Drools sessions (where the instances are reused) using Apache Commons Pooling as suggested in that same nabble thread...I can't use a Singleton because the REST service needs to be fast and I don't want other requests to be potentially blocked if one request takes longer...and so far that seems to work for me.