tag:blogger.com,1999:blog-5869426.post6128702161231941414..comments2024-03-25T09:15:58.430+00:00Comments on Drools & jBPM: Shadow Facts - What you always wanted to know, but was afraid to askMark Proctorhttp://www.blogger.com/profile/03304277188725220501noreply@blogger.comBlogger18125tag:blogger.com,1999:blog-5869426.post-84359456888033583712020-09-09T09:18:48.679+01:002020-09-09T09:18:48.679+01:00Awesome blog. I enjoyed reading your articles. Thi...Awesome blog. I enjoyed reading your articles. This is truly a great read for me. I have bookmarked it and I am looking forward to reading new articles. Keep up the good work! <a href="https://milkio.co.nz/ghee-private-label/" rel="nofollow">Private Label</a><br />Top SEOhttps://www.blogger.com/profile/15733315240949561182noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-747866908365466032020-06-03T07:26:21.976+01:002020-06-03T07:26:21.976+01:00It has fully emerged to crown Singapore's sout...It has fully emerged to crown Singapore's southern shores and undoubtedly placed her on the global map of residential landmarks. I still scored the more points than I ever have in a season for GS. I think you would be hard pressed to find somebody with the same consistency I have had over the years so I am happy with that. <a href="http://kameronjszhs.blogzag.com/18579403/indicators-on-loto-you-should-know" rel="nofollow">xstd</a><br />Top SEOhttps://www.blogger.com/profile/15733315240949561182noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-48578322731424803902009-04-02T01:54:00.000+01:002009-04-02T01:54:00.000+01:00We have a new rete algorithm that no longer needs ...We have a new rete algorithm that no longer needs them while working with java classes. Do a search on asymmetrical rete.Mark Proctorhttps://www.blogger.com/profile/03304277188725220501noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-71433411916945608152009-04-01T22:41:00.000+01:002009-04-01T22:41:00.000+01:00What happened with Shadow Facts and V5?What happened with Shadow Facts and V5?Michael Fingerhttps://www.blogger.com/profile/07576490613438647850noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-67879403676354690242008-02-22T13:49:00.000+00:002008-02-22T13:49:00.000+00:00GemStone OODB used modified version of VM (in old ...GemStone OODB used modified version of VM (in old days for Smalltalk, later for Java) that automatically marked "dirty" objects when field values got changed. Their approach was so much better from performance and transparency perspectives, it's a pity Sun did not make it a standard VM feature. The beneficiaries would be all persistence and UI frameworks, rule engines would get some boost as wellsnshorhttps://www.blogger.com/profile/03389239488316044838noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-64224432416310202542008-02-21T16:33:00.000+00:002008-02-21T16:33:00.000+00:00handling modify in block is much faster for most c...handling modify in block is much faster for most cases, but there is a niche case where it is better. For most of the applications I've worked on, modify usually involved multiple attributes.<BR/><BR/>In the case where modify is only 1 attribute, it's faster to just do the modify. The other thing is some times people do stupid things like call the set method with the same value. In JESS, it does the comparison before doing the modify to avoid doing useless work.<BR/><BR/>The code that castor generates compares the old and new value before modifying. For some really simple use cases, the alternate approach of doing a modify directly in the set method "may" be faster, but honestly I don't think it is worth it.<BR/><BR/>You'd end up with an implementation that is slow for most cases and only faster for a niche case. I mention it just for discussion purposes and definitely wouldn't recommend it.<BR/><BR/>A lot of different technqies have been tried over the years. On a semi-related note, Dr. forgy tried using non-blocking hashmaps with parallel RETE several years back.woolfelhttps://www.blogger.com/profile/13814445471254728002noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-66706240349413910272008-02-21T15:39:00.000+00:002008-02-21T15:39:00.000+00:00Engine notifications on a per field change is a ba...Engine notifications on a per field change is a bad idea, as it massively increases the amount of work you need to do. You need that done as part of a block modification, as we allow in MVEL.<BR/><BR/>One idea, which the micro container does, is to use annotations to define the transaction boundaries. It will then determine which objects have changed in that boundary, typically a method, and do the notifications at the end of the method. Because of our new asymmetrical algorithm we can sill do the retract successfully after the field has changed.Mark Proctorhttps://www.blogger.com/profile/03304277188725220501noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-65066402306906007052008-02-21T15:35:00.000+00:002008-02-21T15:35:00.000+00:00It's not a modify in place, or what I call a "true...It's not a modify in place, or what I call a "true modify", it's still doing a retract+assert, but the retract does not need to recalculate the joins, there are enough references that it can now just iterate down the references clearing out the memory. so the retract is protected from any possible field changes on the object being retracted. This makes the retract+assert process asymmetrical instead of symmetrical.Mark Proctorhttps://www.blogger.com/profile/03304277188725220501noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-80535811993372463352008-02-21T13:54:00.000+00:002008-02-21T13:54:00.000+00:00I just double checked from an old JRules paper. He...I just double checked from an old JRules paper. Here is what it says.<BR/><BR/>3.7 Rete Tuning<BR/>The Just-In-Time (JIT) byte code compilation feature for Rete mode can be used to optimize the evaluation of conditions of rules. When JIT is enabled JRules will call methods in the conditions of rules using generated byte code, rather than using the Java reflection APIs. The time to create an IlrContext from an IlrRuleset is increased when the JIT is enabled, however this is a one time cost for pooled IlrContext instances, and should not be of concern. The JIT can be activated whenever the rule engine has the security permissions to create a custom ClassLoader.<BR/><BR/>This is just me guessing here, so it likely to be wrong. From that description JIT is a one time cost up front at rule compilation time. since it mentions a custom classloader, it sounds like it's doing something similar to drools extractors.<BR/><BR/>for some reason I mis-interpreted jrules JIT and thought it was like java JIT, where code is modified at runtime. Silly me, not all JIT means the same, especially when it's used liberally.<BR/><BR/>on the topic of modify, there's another alternative. In a normal bean that implements java beans property listeners, the set method notifies the listeners and pass the old and new value. An alternative to that is to do something like this.<BR/><BR/>public void setName(String name) {<BR/> Rete.retract(this);<BR/> this.name = name;<BR/> Rete.assert(this);<BR/>}<BR/><BR/>this is also an old idea. I forgot where I came across it, but it's been floating around since 2004 also. Of course, this is different than modifying in place.woolfelhttps://www.blogger.com/profile/13814445471254728002noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-63400018903343942172008-02-21T13:44:00.000+00:002008-02-21T13:44:00.000+00:00Thanks much, Edson! This was a great post-- very ...Thanks much, Edson! This was a great post-- very informative.<BR/><BR/>RickRickhttps://www.blogger.com/profile/13428944430725185873noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-73170793816037811672008-02-21T13:35:00.000+00:002008-02-21T13:35:00.000+00:00I should explain what I mean by direct access. It'...I should explain what I mean by direct access. It's from past research by Dr. Forgy. Rather than have extractor or helpers, the nodes directly access the attribute. What that means the node doesn't need to use an macros or helper classes. In a generic implementation, the alpha node might have<BR/><BR/>assertFact(Object fact)<BR/><BR/>Using direct access, the assert method signature varies depending on the node.<BR/><BR/>assertFact(Customer fact)<BR/>assertFact(Address fact)<BR/><BR/>For the joins, the node still needs to do a type cast, but it would avoid using a macro to access the value. If I find the old paper, I'll post on jamocha's SVN.<BR/><BR/>If I'm not mistaken, JRules does runtime bytecode generation to optimize performance. Drools does it with the builder, so that means it's a compile time optimization right? Atleast that's my guess based on JRules documentation.<BR/><BR/>In JESS and Jamocha, there isn't any type cast, since it's all deffacts internally. The cost is up front when the java object is converted to deffact.<BR/><BR/>In the case of JESS, the Fact object isn't an interface, so it makes it hard for ernest to support non-shadowed fact.<BR/>http://herzberg.ca.sandia.gov/jess/docs/71/api/jess/Fact.html<BR/><BR/>In terms of doing modification in place versus doing a retract/assert cycle, most engines don't modify in place. if you've gotten it to work in place that's cool. it would be interesting for everyone to see what kind of gains it produces versus code complexity. The benefit of retract/assert is the design and code is much simpler. doing modification in place tends to result in more complex code, so in the long run it may have un-intended side effects. When one considers NOT, Exist, Collect, Accumulate, Temporal and Aggregate nodes, it may not be optimal in all cases to modify in place.<BR/><BR/>I think one area of research which many people haven't focused on is RETE topology. I think there's still ways to optimize performance through optimizing the topology. From past experience fixing bugs in Jamocha, improving topology tends to have much greater gains than optimizing modify. I know that ernest has improved modify performance over the years, but they tend to be small.woolfelhttps://www.blogger.com/profile/13814445471254728002noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-68589335086832224632008-02-21T09:34:00.000+00:002008-02-21T09:34:00.000+00:00Our engine has always been model independent, via ...Our engine has always been model independent, via the Extractor interface. That means it is up to the builder to determine how to read fields, both reflection and bytecode would be valid, the engine simply does not care. We generate direct bytecode getters with asm to ensure maximimum performance - we have done this since Drools 3.0. Drools 4.0.x introduced primitive support to avoid Object wrapping, and provides full co-ercion on all of the get methods. i.e. if you call getObject( object ) instead of getInt( object ) on an Extractor for a primitive it will wrap that and return Integer.<BR/><BR/>so if you had Person( name == "bill" )<BR/>That will generate and cache an Extractor instance<BR/><BR/>PersonNameExtractor implements Extractor {<BR/> Object getValue(Object object) {<BR/> return ((Person) object).getName();<BR/>}<BR/><BR/>This allows for how we read the model to be independent on whether a user has turned on shadow proxies or not. Shadow proxies can be turned on/off global or for specific instances.<BR/><BR/>Drools 5.0 will do away with the need for shadow proxies at least with regards to being able to retract an object when the field(s) have changed externally to the engine. Drools 4.0, like Jess, is symmetrical in that it must recalculate the joins to perform the retract and thus the need for the preserved data. Drools 5.0 will be asymmetrical and will not need to re-calculate the joins and thus is not impacted by any possible field changes. We tried this with Drools 3.0 and had performance problems, those are now addressed for 5.0 - and with performance gains :) So that will simplify most people's use cases.Mark Proctorhttps://www.blogger.com/profile/03304277188725220501noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-47858690051431074232008-02-21T05:07:00.000+00:002008-02-21T05:07:00.000+00:00Microsoft's BRE suffers from the inconsistency iss...Microsoft's BRE suffers from the inconsistency issue. In fact, Charles young filed a bug with Microsoft related to how they handle modify. Instead of doing a retract, alter object and assert, they do something else. That results in incorrect behavior, so the fact doesn't get removed properly.<BR/><BR/>JRules 6 already uses byte code modification to access the attributes directly. I don't know exactly how they do it, but that feature has been in JRules since 6.0 if I'm not mistaken.woolfelhttps://www.blogger.com/profile/13814445471254728002noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-22230981354665568582008-02-21T04:44:00.000+00:002008-02-21T04:44:00.000+00:00yet more examples that mutation is of the Devil !yet more examples that mutation is of the Devil !Michael Nealehttps://www.blogger.com/profile/14670410000512777421noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-34532367329584242232008-02-21T04:18:00.000+00:002008-02-21T04:18:00.000+00:00There's actually an old post going back to 2004 on...There's actually an old post going back to 2004 on JESS mailing list that mentions the byte code technique, but I don't have a link to it. On a related note, I used Castor to compile schemas to clases with java beans support for this reason. If all users follow good rule development practices, the need for shadow facts is reduced. With direct method access at the node level, performance and memory usage should improve by 20-30%.woolfelhttps://www.blogger.com/profile/13814445471254728002noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-80450007636764362662008-02-21T04:11:00.000+00:002008-02-21T04:11:00.000+00:00There is a way to not use shadow facts or cache th...There is a way to not use shadow facts or cache the attribute values, even for cases where the objects are mutable.<BR/><BR/>If you use byte code modification to inject code into all the set method of the object so that all changes trigger an modify, you can do without the shadow. I thought of the idea back in 2003/2004 when I was thinking about Laddad's book.<BR/><BR/>The other reason for shadow facts is it boosts performance. You'll see the same kind of improvement in Jrules and MS BRE. To achieve the same performance without using shadow facts is to do what OPS5 did, compile the nodes to access the attributes directly with the getXXX method.<BR/><BR/>The downside with that approach is the compilation time is much higher and dynamically adding/removing rules takes a huge hit. It's similar to waiting for JSP pages to compile.<BR/><BR/>If you combine both AOP and direct method access at all the nodes, you can completely by pass shadow facts, without having to worry about inconsistent behavior.<BR/><BR/>For those interested, the early versions of OPS5 didn't have the distinction between shadow fact and object, since it was all just facts. It was later that Forgy experimented with directly accessing the attributes.<BR/><BR/>In clips, if you use deffacts directly, there is no shadow, since the facts are not accessible outside of CLIPS.<BR/><BR/>Art introduced shadow facts, if I am not mistaken. ART was written in C++ so they supports object oriented programming. In jamocha, I give users the option of asserting an object with or without shadow, which allows mixed mode.<BR/><BR/>For me, direct method access and byte code modification is too big of a trade off. If the application needs absolute speed, I think it much better to use static analysis or optimization algorithms instead. If the engine gives the user the flexibility of asserting w shadow, the user can choose for themselves. Having shadows turned on/off globally also runs into limitations. I've had cases where half the data was static, but the other half was mutable.<BR/><BR/>It's useful to be able to call setXXX method and have it automatically notify the engine and trigger a modify. That's one feature I wish JESS had, but so far it still doesn't have it. there's an old paper on OPS5 that talks about compiling the nodes to access attributes directly.woolfelhttps://www.blogger.com/profile/13814445471254728002noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-26566154539955075692008-02-21T02:41:00.000+00:002008-02-21T02:41:00.000+00:00If you have immutable objects you can turn off sha...If you have immutable objects you can turn off shadow proxies and not have to deal with them. Trying to keep your data models immutable is nice, but not always possible :(<BR/><BR/>I'll post my email to the user mailing list, which gives a bit more information on the subject:<BR/><BR/>Drools 4.x assert/retracts, like Jess, are symmetrical. When you assert the data it uses the constraints to determine the cross product joins which controls the propagation throughout the network. When you do a retract it uses those same constraints to determine the cross product joins to be able to propagate through the graph removing itself from the node memories where the assert propagation reached before.<BR/><BR/>Now if we go back to standard Java when you have a HashMap with a key/value pair, if you put an object into the map and changed a field on the key that changes the hashcode/equals methods you will never be able to retrieve the object. It is the same for us, the working memory has lots and lots of hash maps.<BR/><BR/>Now what happens when you modify a bean? a modify is actually a retract+assert so when you call update(...) after the field has changed how can the retract correctly determine all the nodes where the object is remembered? It can't as the information is no longer there to determine this. To get around this we have to shadow a bean, i.e. make a copy of all the bean's fields on insertion. When you call update() the engine still sees the old field values, even though they might have been updated on the source object. This allows the retract to always behave symmetrically to the assert. On the assert part of the update() we refresh the shadow proxy with the new field values.<BR/><BR/>With Maps and thus nested objects shadowing becomes very hard. As we would have to shadow the entire map and then shadow evey object in that map. Which is far from ideal. This is also why with 4.0 you must be extremely careful with nested objects, which we recommend are immutable while the parent object lives in the network, shadowing an entire object graph is simply not practical.<BR/><BR/>In Drools 3.0 we did not need to shadow as we hade a more complicated algorithm that maintained more references allowing for asymetrical behaviour - however I couldn't get the algorithm to perform with Jess like performance and it used a lot more memory. I dropped Drools 4.0 back to how Jess does it with shadow facts and symmetrical assert/retract. For Drools 5.0 I have re-written the algorithm and have managed to get asymettrical behaviour with actual performance increases, not losses. This is now in a branch and I hope to merge into trunk soon.<BR/><BR/>After this the only reason someone would want shadow proxies is to protect an object that is currently in the working memory from field modifications leaving the network integrity invalid. i.e. if another thread changes the field on an object in the working memory and the wm is not correctly notified we have an object cached in a state that it no longer represents, this will make all join attempts invalid until the engine is notified and the object is re-propagated throughout the network. As 5.0 will no longer require shadow proxies by default we will propably remove the asm proxy implementation we have and instead just have an interface and leave the implementation of this to the user, probably using aop. This will simplify our engine, and help performance a little, so that we offload the solution to problem to those that need it.Mark Proctorhttps://www.blogger.com/profile/03304277188725220501noreply@blogger.comtag:blogger.com,1999:blog-5869426.post-73941297433417705022008-02-21T02:23:00.000+00:002008-02-21T02:23:00.000+00:00Shouldn't this be the default?Why do rule engines ...Shouldn't this be the default?<BR/><BR/>Why do rule engines allow the changing of fact attributes? Webster defines a fact as a "thing done." You can change your opinion, but you can't change facts. Thus you shouldn't be able to change fact attributes.Godmar Backhttps://www.blogger.com/profile/05064247891002006433noreply@blogger.com