Uploaded image for project: 'JBRULES'
  1. JBRULES
  2. JBRULES-3719

Broken accumulate function - possible WM corruption

    Details

    • Type: Bug
    • Status: Closed (View Workflow)
    • Priority: Critical
    • Resolution: Duplicate Issue
    • Affects Version/s: None
    • Fix Version/s: 5.4.0.Final
    • Component/s: None
    • Labels:
      None
    • Environment:

      Linux x86_64, Oracle JDK 7

      Description

      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:

      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
      

      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:

      = 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
      

      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:

      = 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
      

      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:

      = 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) ...
      

      In the output you can see all constraints that were created base on the WM content.

        Gliffy Diagrams

          Attachments

            Issue Links

              Activity

                People

                • Assignee:
                  ge0ffrey Geoffrey De Smet
                  Reporter:
                  mvecera Martin Vecera
                • Votes:
                  0 Vote for this issue
                  Watchers:
                  4 Start watching this issue

                  Dates

                  • Created:
                    Updated:
                    Resolved: