Почему Drools выполняет неправильные правила, когда несколько KieBase выполняются параллельно? - PullRequest
0 голосов
/ 17 февраля 2020

Drools, похоже, дает неправильные результаты, когда несколько процессов выполняются параллельно, и в каждом процессе каждый раз создается и обрабатывается новый объект KieBase.

Пробуется с версиями: 6.5.0.Final, 7.32.0.Final

Подробности:

Я выполнил 120 задач параллельно (используя 7 потоков). Из этих 120 задач слюни дали правильный результат для 108 задач, но выполнили неправильное правило для 12 задач (количество таких неудачных задач варьируется при каждом запуске).

Позвольте мне выложить код и вывести здесь:

    public class TempClass {
        public List<String> droolLogging = new ArrayList<>();
    }

   public void execute(){
        Map<String, List<String>> failedTasks = new ConcurrentHashMap<>(); // to see which tasks were incorrectly executed

        // Run 120 tasks in parallel using x threads (x depends upon no of processor)

        IntStream.range(1, 120).parallel()
        .forEach(taskCounter -> {

            String uniqueId = "Task-"+taskCounter;

            TempClass classObj = new TempClass();

            String ruleString = "package com.sample" + taskCounter + "\n" +
                    "import com.TempClass\n" +
                    "\n" +\
                    "rule \"droolLogging"+taskCounter+"\"\n" +
                    "\t when \n" +
                    "\t\t obj: TempClass(true)\n" +
                    "\t then \n" +
                    "\t\t obj.droolLogging.add(\"RuleOf-"+uniqueId+"\");\n" +
                    "\t end\n";

            // Above ruleString contains 1 rule and it is always executed. 
            // After execution, it will add an entry in array list 'droolLogging' 
            // of class 'TempClass'. In this entry, we are storing task counter 
            // to see rule of which task is executed.


            //following line of code seems to be the culprit as this is somehow returning incorrect KieBase sometime.

            KieBase kbase = new KieHelper()
                             .addContent(ruleString, ResourceType.DRL)
                             .build();

        /*
        //Same issue occurs even if I create different file with different name instead of using KieHelper.

        KieServices ks = KieServices.Factory.get();
        KieFileSystem kfs = ks.newKieFileSystem();

        String inMemoryDrlFileName = "src/main/resources/inmemoryrules-" + taskCounter + ".drl";

        kfs.write(inMemoryDrlFileName, ruleString);

        KieBuilder kieBuilder = ks.newKieBuilder(kfs).buildAll();
        KieContainer kContainer = ks.newKieContainer(kieBuilder.getKieModule().getReleaseId());
        KieBaseConfiguration kbconf = ks.newKieBaseConfiguration();
        KieBase kbase = kContainer.newKieBase(kbconf);
        */


            StatelessKieSession kieSession = kbase.newStatelessKieSession();
            kieSession.execute(classObj);

            System.out.println("(" + Thread.currentThread().getName() + ") " +
                    uniqueId + "_" + classObj.droolLogging );

            //Important: 
            //  To see if correct rule is executed, task no. printed by variable 'droolLogging'
            //  should match with uniqueId

            if(classObj.droolLogging == null || classObj.droolLogging.size() != 1 ||
                    !classObj.droolLogging.get(0).endsWith(uniqueId)) {
                failedTasks.put("" + taskCounter, classObj.droolLogging);
            }
        });

        logger.info("Failed:\n {}", failedTasks);
    }


OUTPUT:
    (ForkJoinPool.commonPool-worker-1) Task-37_[RuleOf-Task-4]
    (ForkJoinPool.commonPool-worker-6) Task-8_[RuleOf-Task-4]
    (ForkJoinPool.commonPool-worker-3) Task-18_[RuleOf-Task-4]
    (ForkJoinPool.commonPool-worker-2) Task-108_[RuleOf-Task-4]
    (main) Task-78_[RuleOf-Task-4]
    (ForkJoinPool.commonPool-worker-7) Task-52_[RuleOf-Task-4]
    (ForkJoinPool.commonPool-worker-4) Task-97_[RuleOf-Task-4]
    (ForkJoinPool.commonPool-worker-5) Task-4_[RuleOf-Task-4]
    (ForkJoinPool.commonPool-worker-3) Task-19_[RuleOf-Task-19]
    (ForkJoinPool.commonPool-worker-5) Task-5_[RuleOf-Task-5]
    (ForkJoinPool.commonPool-worker-2) Task-109_[RuleOf-Task-109]
    (ForkJoinPool.commonPool-worker-7) Task-53_[RuleOf-Task-53]
    (ForkJoinPool.commonPool-worker-1) Task-38_[RuleOf-Task-38]
    (ForkJoinPool.commonPool-worker-4) Task-98_[RuleOf-Task-98]
    .... more

    Failed (12):
        {88=[RuleOf-Task-77], 78=[RuleOf-Task-4], 68=[RuleOf-Task-60], 37=[RuleOf-Task-4], 15=[RuleOf-Task-1], 18=[RuleOf-Task-4], 7=[RuleOf-Task-11], 8=[RuleOf-Task-4], 108=[RuleOf-Task-4], 71=[RuleOf-Task-76], 52=[RuleOf-Task-4], 97=[RuleOf-Task-4]}

This shows:

 - Rule of task 77 was executed in task 88
 - Rule of task 4 was executed in task 78
 - Rule of task 60 was executed in task 68
 - ....

This is wrong. For correct results, in each process, Rule of task X should be executed in task X only.

Есть идеи, что может быть причиной этого?


Обновление: Приведенный выше код предназначен только для тестирования, чтобы увидеть, как генерация и выполнение KieBase будут вести себя в многопоточной среде. Фактический вариант использования выглядит следующим образом:

Вариант использования:

У нас есть набор правил, относящихся к категории. Для каждой категории должен быть выполнен определенный набор правил.

Example:

 for category 1 , I need to execute rule101, rule102, rule103

 for category 2 , I need to execute rule201, rule202, rule203
 ....

 Note: During evaluation, rules of category X should NOT interfere with Rules of category Y, i.e., they should be run independently.

Поскольку нет. категорий огромные, мы строим KieBases (для каждой категории) параллельно и храним его в течение x минут. Через x минут мы проверяем, были ли изменены правила для какой-либо категории, если изменилось, KieBase снова скомпилируется для этих категорий (что снова будет параллельно).

Кроме того, новые категории могут быть добавлены при запуске время. Таким образом, для новых добавленных категорий также соблюдается вышеуказанная процедура.

   category1 -> KieBase1   (compiled rules: rule101, rule102, rule103)
   category2 -> KieBase2   (compiled rules: rule201, rule202, rule203)
   category3 -> KieBase3

   Note: As already mentioned above, execution of KieBase X should NOT interfere with execution of KieBase Y as KieBases are created category wise and for each category, only particular set of rules should be executed.

1 Ответ

0 голосов
/ 11 марта 2020

Наконец, в Drools возникает ошибка, поскольку KieHelper небезопасно для использования в многопоточной среде.

После того, как один из членов сообщества разработчиков Drools предложил решение, Кажется, что причиной вышеупомянутой аномалии и обходного пути для ее устранения является следующее:

Root причина проблемы : KieHelper создает KieModule с тем же идентификатором выпуска по умолчанию.

Решение : Используйте разные идентификаторы выпуска для каждой сборки.

Код: (используйте это в упомянутом выше коде)

KieServices ks = KieServices.Factory.get();
KieFileSystem kfs = ks.newKieFileSystem();
kfs.write("src/main/resources/rules.drl", ruleString);

ReleaseId releaseId = ks.newReleaseId("com.rule", "test" + taskCounter, "1.0.0");
kfs.generateAndWritePomXML(releaseId);

KieBuilder kieBuilder = ks.newKieBuilder(kfs).buildAll();
Results results = kieBuilder.getResults();

if (results.hasMessages(Message.Level.ERROR)) {
  throw new RuntimeException(results.getMessages().toString());
}

KieContainer kContainer = ks.newKieContainer(releaseId);
KieBase kbase = kContainer.newKieBase(ks.newKieBaseConfiguration());

StatelessKieSession kieSession = kbase.newStatelessKieSession();
kieSession.execute(classObj);

Проверьте это, запустив> 500 процессов параллельно, и проблема не возникла.

...