-
Quality Risk
-
Resolution: Done
-
Major
-
5.5.0.Final
-
None
-
None
In our web system, we are creating new StatefulKnowledgeSession per user session and executing a set of rules containing trait donning. This starts to hang (seemingly indefinitely) with following type of stack trace (usually initiated from insert/update):
java.lang.Thread.State: RUNNABLE
at org.drools.core.util.TripleStore.getAll(TripleStore.java:188)
at org.drools.factmodel.traits.TripleBasedStruct.getTriplesForSubject(TripleBasedStruct.java:165)
at org.drools.factmodel.traits.ThingorgdroolsfactmodeltraitsTraitSession$ItemWrapperProxyWrapper.hashCode(Unknown Source)
at org.drools.factmodel.traits.TraitProxy.hashCode(TraitProxy.java:110)
at org.drools.factmodel.traits.ThingorgdroolsfactmodeltraitsTraitSession$ItemWrapperProxy.hashCode(Unknown Source)
at org.drools.common.DefaultFactHandle.<init>(DefaultFactHandle.java:110)
at org.drools.common.DefaultFactHandle.<init>(DefaultFactHandle.java:98)
at org.drools.common.TraitFactHandle.<init>(TraitFactHandle.java:26)
at org.drools.reteoo.ReteooFactHandleFactory.newFactHandle(ReteooFactHandleFactory.java:80)
at org.drools.common.AbstractFactHandleFactory.newFactHandle(AbstractFactHandleFactory.java:68)
at org.drools.common.AbstractFactHandleFactory.newFactHandle(AbstractFactHandleFactory.java:53)
at org.drools.common.NamedEntryPoint.createHandle(NamedEntryPoint.java:765)
at org.drools.common.NamedEntryPoint.insert(NamedEntryPoint.java:301)
at org.drools.common.AbstractWorkingMemory.insert(AbstractWorkingMemory.java:903)
Here is a unit test for org.drools.factmodel.traits.TraitTest. It's not perfect because it will spawn thread that might never die and may need thread/iteration count increased to fail consistently, but capturing a stack trace a few seconds in should show all threads in "getAll()":
@Traitable public static class Item { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } } public static class TraitRulesThread implements Runnable { int threadIndex; int numRepetitions; StatefulKnowledgeSession ksession; public TraitRulesThread(int threadIndex, int numRepetitions, final StatefulKnowledgeSession ksession) { this.threadIndex = threadIndex; this.numRepetitions = numRepetitions; this.ksession = ksession; } public void run() { for (int repetitionIndex = 0; repetitionIndex < numRepetitions; repetitionIndex++) { final Item i = new Item(); i.setId(String.format("testId_%d%d", threadIndex, repetitionIndex)); ksession.insert(i); ksession.fireAllRules(); } } } @Test public void testMultithreadingTraits() throws InterruptedException { final String s1 = "package test;\n" + "import org.drools.factmodel.traits.TraitTest.Item;\n" + "declare Item end\n" + "declare trait ItemStyle\n" + " id: String\n" + " adjustable: boolean\n" + "end\n" + "rule \"Don ItemStyle\"\n" + " no-loop true\n" + " when\n" + " $p : Item ()\n" + " not ItemStyle ( id == $p.id )\n" + " then\n" + " don($p, ItemStyle.class);\n" + "end\n" + "rule \"Item Style - Adjustable\"" + " no-loop true" + " when" + " $style : ItemStyle ( !adjustable )" + " Item (" + " id == $style.id " + " )" + " then" + " modify($style) {" + " setAdjustable(true)" + " };" + "end"; final KnowledgeBase kbase = getKnowledgeBaseFromString(s1); // might need to tweak these numbers. often works with 7-10,100,60, but often fails 15-20,100,60 int MAX_THREADS = 20; int MAX_REPETITIONS = 100; int MAX_WAIT_SECONDS = 60; final ExecutorService executorService = Executors.newFixedThreadPool(MAX_THREADS); for (int threadIndex = 0; threadIndex < MAX_THREADS; threadIndex++) { executorService.execute(new TraitRulesThread(threadIndex, MAX_REPETITIONS, kbase.newStatefulKnowledgeSession())); } executorService.shutdown(); executorService.awaitTermination(MAX_WAIT_SECONDS, TimeUnit.SECONDS); final List<Runnable> queuedTasks = executorService.shutdownNow(); assertEquals(0, queuedTasks.size()); assertEquals(true, executorService.isTerminated()); }