]
Martin Vecera updated JBRULES-3337:
-----------------------------------
Priority: Critical (was: Major)
Broken accumulate function - possible WM corruption
---------------------------------------------------
Key: JBRULES-3337
URL:
https://issues.jboss.org/browse/JBRULES-3337
Project: Drools
Issue Type: Bug
Security Level: Public(Everyone can see)
Components: drools-core (expert), drools-planner
Affects Versions: 5.3.0.Final, 5.3.1.Final, 5.4.0.Beta1
Environment: Linux x86_64, Oracle JDK 7
Reporter: Martin Vecera
Assignee: Geoffrey De Smet
Priority: Critical
Attachments: Drooling-Tournaments-bug.tar.gz
Using Drools Planner, I created a planning solution for planning matches on tournaments.
When I created a score calculating rule using the accumulate function, I realized that it
behaves very strange. This is possibly because of Working Memory corruption. I think so,
because I need to performa several changes in WM to recreate the problem. When I use only
the last state of WM and put it in a freshly instantiated WM, it works fine.
Attached is an reproducer application. You can just unzip and run 'mvn test'. The
test does not fail, but you can see the corruption in its output.
First, I would like to describe the solution. There are the following entities - Team,
Group (a group of teams, usually only teams in the same group play together), Court, Match
(between teams), and Slot (Slot connects a numbered time slot on a particular Court). A
Match can be assigned to a Slot to denote which teams are supposed to play a match at the
given court in the given time.
All of the teams, groups, courts, and maches appear in the WM at the beginning. Planner
then adds slots and tries to assign Matches to the slots.
The test executed by 'mvn test', fills a WM with specific facts. Then it adds a
Slot with some assignment and fires all rules. After that, different match is assigned to
the slot and all rules are fired again. The last step is repeated several times.
The problematic rule is the following one:
{noformat}
rule "Get overhead per team"
when
$t: Team()
accumulate(
$s: Slot($num: number, match != null, $ts: teams, teams contains $t),
$min: min($num),
$max: max($num),
$slist: collectList($s),
$sset: collectSet($s),
$tslist: collectList($ts), // this collects all team sets, each set
$count: count($s)
)
then
System.out.println("= START = rule Get overhead per team");
System.out.println("Working with team " + $t.toString() + ", it
must appear in each subset of the following set: " +
Arrays.toString($tslist.toArray()));
System.out.println("The same team must appear in the match associated with
each of the following slots:");
System.out.println(Arrays.toString($slist.toArray()));
System.out.println("Each slot should be considered only once, we should not
have duplicates in the previous list.");
System.out.println("= END = rule Get overhead per team");
// ((max - min + 1) / min_slots_per_match) - count
insertLogical(
new IntConstraintOccurrence("teamOverhead",
ConstraintType.NEGATIVE_SOFT,
$sset.size() < 2 ? 0 :
Math.max(0, (($max.intValue() - $min.intValue() + 1) / 2 -
$sset.size())), $t, $slist)
);
end
{noformat}
Its main goal is for each team to collect all slots that has a match assigned in which
this team has to play (Slot -> Match -> Teams_in_match contains Team).
Some data is collected for each Slot - most of these are just to show the bug. As you can
see, only Slots that fulfills the condition 'teams contains $t' can be taken into
account.
$ts refers to the set teams in this Slot. A list of these sets is collected ($tslist). On
the RHS, this rule contains some debug output. It mainly prints out the team ($t) and the
list of team sets (list of all $ts). According to the rule condition, each set in $tslist
must contain the team $t. However, this is not true as you can see from the test output
after 4th rules firing:
{noformat}
= START = rule Get overhead per team
Working with team Team X0, it must appear in each subset of the following set: [[Team X1,
Team X2]]
The same team must appear in the match associated with each of the following slots:
[Slot [Court A, 0, match=Match [teamsInMatch=[Team X1, Team X2]]]]
Each slot should be considered only once, we should not have duplicates in the previous
list.
= END = rule Get overhead per team
{noformat}
Team X0 does not appear in [Team X1, Team X2]. Because of that, wrong Slots are taken
into account and the rule breaks the score.
Another problem is that the list of collected slots ($slist) and the set of collected
slots ($sset) differ in size. The list simply contains some duplicates and this means that
some Slots were considered multiple times. This can be seen after 3nd rules firing:
{noformat}
= START = rule Get overhead per team
Working with team Team X0, it must appear in each subset of the following set: [[Team X0,
Team X2], [Team X0, Team X3]]
The same team must appear in the match associated with each of the following slots:
[Slot [Court A, 0, match=Match [teamsInMatch=[Team X0, Team X3]]], Slot [Court A, 0,
match=Match [teamsInMatch=[Team X0, Team X3]]]]
Each slot should be considered only once, we should not have duplicates in the previous
list.
= END = rule Get overhead per team
{noformat}
You can see the duplicate Slot in the list. I tried running equals() on them and they are
equal. On the other hand, a Slot with teams X0 and X2 must have been considered because it
appears in the list of $ts sets ([[Team X0, Team X2], [Team X0, Team X3]]). How is this
possible? We no longer have a Slot assigned with the match between team X0 and X2.
Each step of rules firing produces an output in the following format:
{noformat}
= Run no. X ============================================
= START = rule Get overhead per team
...
= END = rule Get overhead per team
... several executions of the rule ...
X) ... content of WM after firing the rules...
X) ...
{noformat}
In the output you can see all constraints that were created base on the WM content.
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: