-
Bug
-
Resolution: Done
-
Major
-
6.0.1.Final
-
None
-
None
Ciao, in the documentation about "reuse Java methods" Ref. (1) a java.lang static method is used as a pattern.
- A-/ I hereby attach proof Java code to replicate the issue that doing so, rules will functionally work OK, actually, but in the background will increase memory consumption leading to Exception java.lang.OutOfMemoryError: Java heap space
- B-/ I also attach proof of Java code to replicate that by taking the "Java methods" from the pattern and when each one is extracted to their own "conditional element eval", then, it will work OK functionally and memory wise.
Therefore, I believe either of the two:
- The reference (1) is wrong, example should be corrected, and I suggest also should be put a warning that each "reuse Java methods" should get their own "conditional element eval"
- If reference (1) is intended to be working fine, then I suspect there is a bug in the background working of the ReteOO/Phreak algorithm, so I hope this helps identify the bug.
References
(1) http://docs.jboss.org/drools/release/6.0.1.Final/drools-docs/html_single/#d0e7102 example Person( Math.round( weight / ( height * height ) ) < 25.0 )
Java code to replicate
In the attached Maven project, in the src/main/resources there are two .drl files:
- package_usingReuseJavaMethodsExample.drl this will demonstrate point A above
- package_usingCEeval.drl this will demonstrate point B above
Leave only one of the two files with the .drl extension, so to decide which point to prove.
I encourage to run the AppRealtime.class with the following command line parameters: -XX:+HeapDumpOnOutOfMemoryError -Xms128m -Xmx128m -ea. The other two App* are left as I originally suspected this bug was due to the pseudo session clock, but it's not, so you can ignore them.
If running to demonstrate point A, it will after 3 minutes or so Exception java.lang.OutOfMemoryError: Java heap space as per attached screenshots.
If running to demonstrate point B, it will run forever, you can attach Java VisualVM as per attached screenshot.
Business Requirements - explanation of the Rule
Sensor data is received as OpDatum.java, about temperature and heater on/off from xml and instantiated in Java object, put into a list AcmeModelListDTO.java to be inserted into the Working Memory.
- If temperature is < 9, then the heater must be running, otherwise an Alert must be raised.
- If temperature is >= 9, or the heater is running, and there was previously raised an Alert, then the Alert shall be retracted
The attached code is a minimalistic extraction of a bigger broader application, so there are pieces of Java code which could have been likely optimized, in this version.
Style A
This implementation follows example at http://docs.jboss.org/drools/release/6.0.1.Final/drools-docs/html_single/#d0e7102 example Person( Math.round( weight / ( height * height ) ) < 25.0 )
Rules will functionally work OK, actually.
You will see that Java asserts in the AppRealtime.java are always met, and indeed the Working Memory fact count does not explode, the number of facts in the Working Memory is always limited.
But, in the background will increase memory consumption leading to Exception java.lang.OutOfMemoryError: Java heap space
package com.acme.drools6test.sessionclock; /* "reuse Java methods" Ref. [1] Using analogous example from example "reuse Java methods" in the doc at [1]. This implementation, despite follow example documentation, does NOT work good. Functionally, actually works OK. The rules behaves as expected indeed. Problem is however memory consumption is always icreasing, leading to Out of Memory error [1] http://docs.jboss.org/drools/release/6.0.1.Final/drools-docs/html_single/#d0e7102 */ import org.acme.model.* declare OpDatum @role(event) @timestamp(ts) end declare Alert @role(fact) condition : String end rule "AB1 Detect" no-loop dialect "mvel" when $oNumeric : OpDatum( id == "temperature", Double.parseDouble(val) < 9 ) $oBoolean : OpDatum( id == "heater_powered", val == "False" ) not( Alert( condition == "temperature freezing" ) ) then Alert a = new Alert(); a.condition = "temperature freezing"; insert(a); System.out.println( "AB1 Detect Alert : " + a); end rule "AB2 Clear" no-loop dialect "mvel" when $a : Alert( condition == "temperature freezing" ) OpDatum( id == "heater_powered", val == "True" ) or OpDatum( id == "temperature", Double.parseDouble(val) >= 9 ) then retract($a); System.out.println("Alert "+$a+" retracted"); end rule "OpDatum Housekeeping" salience 1000 no-loop dialect "mvel" when $oldOpDatum : OpDatum( $id : id ) $newOpDatum : OpDatum( this after $oldOpDatum, id == $id ) then retract($oldOpDatum); end
Style B
This implementation instead take the "Java methods" from the pattern,
and when each one is extracted to their own "conditional element eval", then, it will work OK functionally and memory wise.
package com.acme.drools6test.sessionclock; /* "reuse Java methods" Ref. [1] Using "import function" but then extracted each one is extracted to their own "conditional element eval" Ref. [2], which is /different/ from example "reuse Java methods" in the doc at [1]. This works OK, functionally and memory wise. [1] http://docs.jboss.org/drools/release/6.0.1.Final/drools-docs/html_single/#d0e7102 [2] http://docs.jboss.org/drools/release/6.0.1.Final/drools-docs/html_single/#d0e8745 */ import function java.lang.Double.parseDouble import org.acme.model.* declare OpDatum @role(event) @timestamp(ts) end declare Alert @role(fact) condition : String end rule "AB1 Detect" no-loop dialect "mvel" when $oNumeric : OpDatum( id == "temperature", $val : val ) eval( parseDouble($val) < 9 ) $oBoolean : OpDatum( id == "heater_powered", val == "False" ) not( Alert( condition == "temperature freezing" ) ) then Alert a = new Alert(); a.condition = "temperature freezing"; insert(a); System.out.println( "AB1 Detect Alert : " + a); end rule "AB2 Clear" no-loop dialect "mvel" when $a : Alert( condition == "temperature freezing" ) OpDatum( id == "heater_powered", val == "True" ) or ( OpDatum( id == "temperature", $val : val ) and eval ( parseDouble($val) >= 9 ) ) then retract($a); System.out.println("Alert "+$a+" retracted"); end rule "OpDatum Housekeeping" salience 1000 no-loop dialect "mvel" when $oldOpDatum : OpDatum( $id : id ) $newOpDatum : OpDatum( this after $oldOpDatum, id == $id ) then retract($oldOpDatum); end
Conclusion
Kindly advise, I hope provided detailed explanation, and useful Java code to replicate.
Thank you.
Ciao, Matteo