[rules-users] Questions about collect performance

Dirk Bergstrom dbergstr at juniper.net
Fri Apr 13 23:23:21 EDT 2007


Edson Tirelli (JIRA) was heard to exclaim, On 04/11/07 05:57:
> Edson Tirelli commented on JBRULES-740:
> Being brief, using the following rule:
> rule "Find all PRs for user"
> when
>  $user : User( )
>  $pr_list : RecordSet( size > 0 ) from
>   collect( PRRecord( $resp : responsible -> ( $user.hasReport($resp) ) ) )
> then
>  $user.setRecords($pr_list, DataSource.GNATS);
> end
> if you already have "User" facts asserted into the working memory, it means
> that for every PRRecord that matches your <constraints>, it will recalculate
> the "collect". So, if you have a User fact, and assert a PRRecord, it will
> create a RecordSet and add your PRRecord to the set. When you assert a second
> PRRecord, it will create a new RecordSet and add the 2 PRRecord. As you see,
> it is a progression: 1+2+3+4...+N. Your case is even worst because your
> constraint is a predicate (not indexable right now), so, it means the
> progression above is executed in full.

As it turns out, this is actually quite fast; it takes 50-100 msec per PR
assertion, so I can cram in 10,000 of them in a few minutes.  Nonetheless, I
understand that it is less than optimal at the moment.

> Anyway, what you need to do:
> 1) If possible, adjust your business model in a way to replace the predicate
> for a regular "==" constraint, avoiding full iteration because of indexing.

There's no way to do that.  What I really want is this:

$user : User( $reports : reports )
$pr_list : RecordSet( size > 0 ) from
 collect( PRRecord( $reports contains responsible ) )

(the syntax could also be "responsible containedIn $reports").

But Drools doesn't (yet?) support that.

For now, I could warp my application such that I could write this:

 collect( PRRecord( responsible_users contains $user ) )

Is that something that Drools could index?

That would introduce some unpleasant dependencies in my code, but if it allows
indexing, it might be worth it.

> 2) Use a control fact to limit the partial matches. Example:
> rule "Find all PRs for user"
> when
>  $control : ControlFact( phase == "findPR" )
>  $user : User( )
>  $pr_list : RecordSet( size > 0 ) from
>   collect( PRRecord( $resp : responsible -> ( $user.hasReport($resp) ) ) )
> then
>  $user.setRecords($pr_list, DataSource.GNATS);
>  $control.setPhase( "do everything else" )
>  modify( $control )
> end
> Doing the above, make sure you assert all PRRecords first and only then, set
> (or assert) your control fact to phase "findPR". The above will make the
> engine avoid wasting time calculating partial matches that will never be
> used. It seems ugly at first, but it is a technique that unfortunately needs
> to be used in some special cases.

That doesn't really fit with the application flow.  I could have a rule like this:

when
 ControlFact( )
 $user : User( )
 $pr_list : RecordSet( size > 0 ) from
  collect( PRRecord( $resp : responsible -> ( $user.hasReport($resp) ) ) )
then
 $user.setRecords($pr_list, DataSource.GNATS);

And retract the ControlFact before asserting PR objects, then assert it when I'm
ready to fireAllRules().  Would that have the desired effect?

Thanks.

-- 
Dirk Bergstrom               dirk at juniper.net
_____________________________________________
Juniper Networks Inc.,          Computer Geek
Tel: 408.745.3182           Fax: 408.745.8905



More information about the rules-users mailing list