Hi Geoffrey,

  Thanks for the additional info.

Regarding the need or not of ShadowFacts, I will try to explain what they are, what is the problem, and how they are used to solve the problem as there are other users that also questioned about them. Hopefully, it will make things clear.

As we know, the Rete algorithm is simply a "network data-structure", composed by nodes and connections between nodes. Some nodes have 1 input (alpha nodes) and other nodes have 2 inputs (beta nodes, that perform joins between the tuple coming from the left and the fact coming from the right). Both node types usually contains constraints that in if evaluate to true will allow the fact/tuple to be propagated to the next node and in if they evaluate to false, will block the propagation.

Now, lets imagine what happens inside a given node, for instance a beta node, when a fact is asserted. Lets use the following example:

rule "r1"
when
  Person( $likes : likes )
  Cheese( type == $likes )
then
  ...
end

As you can see, there will be a beta node (a join in this case) that will get all Person instances (comming from the left side) and try to match with every Cheese instance comming from the right side. We stated in our rule that each person will only match the Cheese instance whose type is the one the person likes ( Person.getLikes() == Cheese.getType() ). So, join node will check this condition for each pair of Person + Cheese. For each pair that evaluates to true, it will propagate, otherwise it will not propagate. So, imagine you assert a person object [ Person( name = "bob", likes = "brie" ) ] and a cheese object [ Cheese ( type = "brie" ) ]. This will obviously match and activate the rule. Then, after that you decide to change the person object that now likes "muzzarela" instead of "brie". It does not matter if this change occurs inside the engine (RHS) or in your application code. Then you call modifyObject( person ) to tell the engine that the person object changed, and of course the rule activation must be canceled. So, your "modifyObject" command will go through the network and reaches the join node we were talking about. Now, the node must check 2 scenarios:

1. Is there any old match that is not valid anymore? If yes, it needs to propagate a "retract" from that node down the network (that will cause the activation to be canceled in the end). 2. Is there any new match that is now valid? If yes, it needs to propagate the new match from that node down the network.

The scenario 2 above is easy and straight forward, but the scenario 1 is the one we are interested. There are basically 2 ways for the node to know if there was an old match that is not valid anymore. He must either:

a. Remember each old match for the Person object

  Or

b. Remember the old value of the Person "likes" attribute

Case "a" above is easy as it just iterates over the matches and in case one of them is not valid anymore, it propagates a retract. For case "b" above, it just uses the old value of the attribute (that it remembers) to recalculate all matches and checks if the match continue valid with the new value.

  Well, that was the theory.
Talking about the implementation now, JBRules 3.0 implements the solution "a" above. In each node of the network, for each tuple and each fact that reaches that node, it remembers each match. It means a complicated data structure with references to objects everywhere. Also, in theory, it is faster than method "b", but the drawback is that this solution uses a LOT of memory to store this match information. In practice, the garbage collector and the object instantiation simply kills performance for the general case.

So, in JBRules 3.1 we implemented solution "b". Now, instead of remembering the matches, JBRules remembers the values for the object's attributes. In theory it is slower, but for the general case it ends up being faster, as it uses a less memory and instantiates a LOT LESS objects. So, how can JBRules remember old object attributes, even if you change an attribute from outside the engine? This is what shadow facts do: ShadowFacts are a shallow copy of your object's attributes, that in JBRules 3.1 are implemented as dynamic proxies with lazy attribute cache. So, once the engine reads an attribute for the first time, it will cache the attribute in the proxy and will only update this cache at safe points.

As stated above, this is better for the general case, but not for ALL cases. If your rule base has a small number of rules, but you assert a huge number of facts in working memory, it may happen that method "a" is better. But again, if your rule base grows, method "a" seriously hurts performance and increases memory consumption. Another special case (and this is the one I was trying to explain) is when you don't change bound/constrained attributes. In the above example, if once a person is asserted you never change the "likes" attribute value ( or the "type" attribute in case of the Cheese ), it means a node will never have a problem calculating its matches even if the value is not cached. Please, node that in the above example you can change Person.name and Cheese.price as much as you want, as they are not "bound" nor "constrained". For this specific case, ShadowFacts are simply overhead and bring no advantage. A real example of this use case is Telecom Usage Events "guiding". When the mediation/rating software for a telecom company is doing the guiding to identify what is the user, service plan, etc for a given usage event, the rules will be constrained by attributes that will not (can not) change. So, for cases like this, ShadowFacts may be disabled as they are only overhead.

Finally, answering your question, if you change a bound/constrained attribute for an object WHILE the object is IN the working memory, then you need ShadowFacts. But, if you have a pattern in your application that, lets say, always retract objects from the working memory, changes its attributes and assert the object again, :) then you don't need ShadowFacts... but I think this use case is not a real use case... is it? :)

So, I think making shadow facts optional may be a good feature to implement.

  Hope the explanation helps.

  []s
  Edson

Geoffrey De Smet wrote:


With kind regards,
Geoffrey De Smet


Edson Tirelli wrote:


  Geoffrey,

My guess is that ShadowProxyFactory is failing to match a method that is declared in a superclass or interface with the overriden method in a subclass. Because of this, it is trying to proxy 2 times the same method. I will take a look in it asap.


thanks Edson, though there is no real rush, but it's probably something critical for the m1 release for most use cases ;)

Anyway, here's my class:

public class Match extends AbstractPersistable
        implements Comparable<Match>, Solution {
    // ...

    // Implemented from Comparable<Match>
    public int compareTo(Match lesson) {

    // Overwritten from Object, implemented from Solution
    public Match clone() {

    // Overwritten from AbstractPersistable
    public String toString() {
}


Regarding your question, what I mean is to use JBRules as a "classification (discrimination) network". It means your rules will simply match objects with the single intent of identifying or grouping the objects and will not change bound/constrained fields in the objects. This is a valid use case, but not that common I guess. And when you don't change bound/constrained field values during the fact lifecycle, you don't need shadow facts.


- I don't modify constraints field in the RHS of rules, but I do modifty them outside the rule engine (in the same thread), just before calling modified + fireAllRules
- I do assert some logical objects in the RHS of rules.

Do either or both of these violate a "classification (discrimination) network"?


   []s
   Edson

Geoffrey De Smet wrote:

http://jira.jboss.com/jira/browse/JBRULES-575

I can't seem to manage to isolate the problem :/
It's not just because I have 2 different references to Team in Match
- because I wrote an integration test with Cheese and CheeseCouple that worked.

See the issue for more info.



I am using drools3 in a single-threaded context - would that suffice to have no need of shadow facts? What does "using it as an discrimination network only" mean in practice?


With kind regards,
Geoffrey De Smet


Edson Tirelli wrote:

  Geoffrey,

Plz, if you can open a JIRA with a sample it would be great. Probably some kind of unexpected method name conflict.

Regarding the feature, right now it is mandatory, but I was talking to Mark to make it optional. Unfortunally Shadow Facts are one of those necessary evils. If you ever change a fact attribute in your network, that fact needs a shadow fact. So, unless you use the engine simply as a discrimination network, you will need them, but the idea is to make them optional and better yet on an object type basis, in a way that users can turn it ON only for those types where it is really needed.

  []s
  Edson

Michael Neale wrote:

Geoffrey, yes that does sound like a bug. The automatic proxy/scheduling is quite new, and will take some time to stabilise - could you create a test case and JIRA, and email me please (with the JIRA, so I make sure it is not lost) ? I will then (with edson) take a look into it, as there will have to be changes to the ASM code that generates the proxy no doubt. I am not sure if proxy/shadowing will be optional or not - haven't been tracking progress with that (in any case it should be transparent to you) - Mark or Edson can fill in on the details for that for 3.2.

Michael.

On 11/30/06, *Geoffrey De Smet* <[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>> wrote:

If it isn't a known bug, I 'll happily make another testcase patch. I am pretty sure it's because I have 2 different references to Team in
    Match, namely homeTeam and awayTeam.

    With kind regards,
    Geoffrey De Smet


    Geoffrey De Smet wrote:
> I just tried the trunk (to compare a benchmark between it and the
    > branch), but I got an error, anything I can do about it?
    > Are shadow facts optional? (or are they better always anyway?)
    >
> java.lang.ClassFormatError: Repetitive field name/signature in class
    > file
net/sf/taseree/samples/travelingtournament/domain/MatchShadowProxy
    >
    >
    > Here's my Match class:
    >
    > public class Match extends AbstractPersistable implements
    > Comparable<Match>, Solution {
    >
    >     private Team homeTeam;
    >     private Team awayTeam;
    >
    >     private Day day;
    >
    >     // getters, setters, clone(), toString()
    >
    > }
    >
    > AbstractPersistable has a Long id with getters/setters
    >


---------------------------------------------------------------------
    To unsubscribe from this list please visit:

        http://xircles.codehaus.org/manage_email






---------------------------------------------------------------------
To unsubscribe from this list please visit:

   http://xircles.codehaus.org/manage_email






---------------------------------------------------------------------
To unsubscribe from this list please visit:

   http://xircles.codehaus.org/manage_email




--
Edson Tirelli
Software Engineer - JBoss Rules Core Developer
Office: +55 11 3124-6000
Mobile: +55 11 9218-4151
JBoss, a division of Red Hat @ www.jboss.com



---------------------------------------------------------------------
To unsubscribe from this list please visit:

   http://xircles.codehaus.org/manage_email

Reply via email to