cancel
Showing results for 
Search instead for 
Did you mean: 

multiInstance won't run task in parallel.

clstokes
Champ in-the-making
Champ in-the-making
I'm trying to execute a task on a collection in parallel, however no combination of settings seems to change the behavior from running sequentially.

Here's my test case in Groovy:
<blockcode>
package tests

import org.activiti.engine.ProcessEngine
import org.activiti.engine.ProcessEngineConfiguration
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.testng.annotations.BeforeClass
import org.testng.annotations.Test

class ParallelActivitiTests {

  @Test
  void test( ) {
    _logger.debug( "Starting…" )
    processEngine.getRuntimeService()
      .startProcessInstanceByKey( "ParallelServiceTest", [ values: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ] )
    _logger.debug( "Completed." )
    Thread.sleep( 30000l )
  }

  @BeforeClass
  void init( ) {
    ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration()
    configuration.setClassLoader( Thread.currentThread().getContextClassLoader() )

    processEngine = configuration
      .setJobExecutorActivate( true ) // allows asynchronous activities
      .buildProcessEngine()

    processEngine.getRepositoryService().createDeployment()
      .addClasspathResource( "activiti-processes/ParallelServiceTest.bpmn20.xml" )
      .deploy()
  }

  private ProcessEngine processEngine
  private static final Logger _logger = LoggerFactory.getLogger( ParallelActivitiTests.class )

}
</blockcode>

And ParallelServiceTest.bpmn20.xml:
<blockcode>
<?xml version="1.0" encoding="UTF-8" ?>
<definitions id="definitions"
             targetNamespace="http://activiti.org/bpmn20"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:activiti="http://activiti.org/bpmn">

  <process id="ParallelServiceTest">

    <startEvent id="start"/>
    <sequenceFlow id="_1" sourceRef="start" targetRef="logValue"/>

<!–
    <serviceTask id="logValue"
                 activiti:class="tasks.LogValueServiceTask"
                 activiti:exclusive="false">
      <multiInstanceLoopCharacteristics
          isSequential="false"
          activiti:collection="${values}"
          activiti:elementVariable="value">
      </multiInstanceLoopCharacteristics>
    </serviceTask>
–>

    <scriptTask id="logValue"
                name="Execute script"
                scriptFormat="groovy"
        >
      <multiInstanceLoopCharacteristics
          isSequential="false"
          activiti:collection="${values}"
          activiti:elementVariable="value">
      </multiInstanceLoopCharacteristics>
      <script>
        System.out.println(execution.getVariable("value"))
      </script>
    </scriptTask>

    <sequenceFlow id="_end" sourceRef="logValue" targetRef="end"/>
    <endEvent id="end"/>

  </process>

</definitions>
</blockcode>

Here's my test case output:
<blockcode>
Gradle test > com.weather.grid.services.platform.environment.ParallelActivitiTests.test STANDARD_OUT
    23:01:49.718 [Test worker] DEBUG c.w.g.s.p.e.ParallelActivitiTests - Starting…
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    23:01:50.391 [Test worker] DEBUG c.w.g.s.p.e.ParallelActivitiTests - Completed.
</blockcode>

I would expect values 0 to 9 to be possibly interleaved, not completely sequential. In my actual code, it's clear that my service task is called sequentially.

Note, neither the serviceTask or scriptTask work and marking both with
activiti:async="true" activiti:exclusive="false"
doesn't change their behavior.

What am I doing wrong?
18 REPLIES 18

clstokes
Champ in-the-making
Champ in-the-making
This is with Activiti 5.13.

clstokes
Champ in-the-making
Champ in-the-making
Created ticket ACT-1814. http://jira.codehaus.org/browse/ACT-1814

jbarrez
Star Contributor
Star Contributor
See http://activiti.org/faq.html#WhatIsTheDifferenceBetweenProcessConcurrencyAndJavaConcurrency

Activiti does not use real concurrency, as it would make using activiti tricky regarding transactions and more specifically syncing when different threads would end. Hence, we decided to opt for the simple approach which so far has saved many people from headaches.

clstokes
Champ in-the-making
Champ in-the-making
I'm sorry for being dense, but what's the practical implication of this?

Does this mean that if I ran two JVM processes that my serviceTask in parallel across those processes?
Or does it mean that if I ran two JVM processes that my serviceTask would distributed across those processes but still sequentially?

Is this the same case when doing a parallel gateway?

Thanks for the help.

trademak
Star Contributor
Star Contributor
The parallel gateway and multiinstance constructs are able to run multiple user tasks in parallel for example. But for service and script tasks they are basically executed serial still. Async can change this behavior if you also set exclusive to false (the default is true). Then the job executor will just execute all jobs available and not serially. So give it a try to set async to true and exclusive to false.

Best regards,

clstokes
Champ in-the-making
Champ in-the-making
I tried setting async to true and exclusive to false on both the scriptTask and multiInstanceLoopCharacteristics and got the same result. What am I doing wrong?

<code>
<?xml version="1.0" encoding="UTF-8" ?>
<definitions id="definitions"
             targetNamespace="http://activiti.org/bpmn20"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:activiti="http://activiti.org/bpmn">

  <process id="ParallelServiceTest">

    <startEvent id="start"/>

    <sequenceFlow id="_1" sourceRef="start" targetRef="printValue"/>

    <scriptTask id="printValue"
                name="Execute script"
                scriptFormat="groovy"
                activiti:async="true"
                activiti:exclusive="false">
      <multiInstanceLoopCharacteristics
          isSequential="false"
          activiti:collection="${values}"
          activiti:elementVariable="value"
          activiti:async="true"
          activiti:exclusive="false">
      </multiInstanceLoopCharacteristics>
      <script>
        System.out.println(execution.getVariable("value"))
        Thread.sleep(3000)
      </script>
    </scriptTask>

    <sequenceFlow id="_end" sourceRef="printValue" targetRef="end"/>

    <endEvent id="end"/>

  </process>

</definitions>
</code>

<code>
13:53:56.580 [Test worker] INFO  o.s.b.f.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [activiti.cfg.xml]
13:53:58.600 [Test worker] INFO  o.a.engine.impl.db.DbSqlSession - performing create on engine with resource org/activiti/db/create/activiti.h2.create.engine.sql
13:53:58.660 [Test worker] INFO  o.a.engine.impl.db.DbSqlSession - performing create on history with resource org/activiti/db/create/activiti.h2.create.history.sql
13:53:58.678 [Test worker] INFO  o.a.engine.impl.db.DbSqlSession - performing create on identity with resource org/activiti/db/create/activiti.h2.create.identity.sql
13:53:58.686 [Test worker] INFO  o.a.engine.impl.ProcessEngineImpl - ProcessEngine default created
13:53:58.687 [Test worker] INFO  o.a.e.impl.jobexecutor.JobExecutor - Starting up the JobExecutor[org.activiti.engine.impl.jobexecutor.DefaultJobExecutor].
13:53:58.690 [Thread-6] INFO  o.a.e.i.j.AcquireJobsRunnable - JobExecutor[org.activiti.engine.impl.jobexecutor.DefaultJobExecutor] starting to acquire jobs
13:53:58.751 [Test worker] INFO  o.a.e.i.bpmn.deployer.BpmnDeployer - Processing resource activiti-processes/ParallelServiceTest.bpmn20.xml
13:53:58.930 [Test worker] INFO  o.a.e.i.bpmn.deployer.BpmnDeployer - Processing resource activiti-processes/GridEnvironment.bpmn20.xml

Gradle test > ParallelJobTest.testResult STANDARD_OUT
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
</code>

trademak
Star Contributor
Star Contributor
I don't think this test is sufficient to come to conclusions. The jobs are inserted in the logical order and executed immediately, so it still does make sense that you get this result. Maybe you can implement some Thread.sleep with an random number of miliseconds?

Best regards,

clstokes
Champ in-the-making
Champ in-the-making
Added a random wait of 0-20 seconds…

<code>
<?xml version="1.0" encoding="UTF-8" ?>
<definitions id="definitions"
             targetNamespace="http://activiti.org/bpmn20"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:activiti="http://activiti.org/bpmn">

  <process id="ParallelServiceTest">

    <startEvent id="start"/>

    <sequenceFlow id="_1" sourceRef="start" targetRef="printValue"/>

    <scriptTask id="printValue"
                name="Execute script"
                scriptFormat="groovy"
                activiti:async="true"
                activiti:exclusive="false">
      <multiInstanceLoopCharacteristics
          isSequential="false"
          activiti:collection="${values}"
          activiti:elementVariable="value"
          activiti:async="true"
          activiti:exclusive="false">
      </multiInstanceLoopCharacteristics>
      <script>
        def random = new Random()
        def wait = random.nextInt(1000)*20
        System.out.println(execution.getVariable("value") + " - waiting " + wait + "ms")
        Thread.sleep(wait)
      </script>
    </scriptTask>

    <sequenceFlow id="_end" sourceRef="printValue" targetRef="end"/>

    <endEvent id="end"/>

  </process>
</definitions>
</code>

<code>
17:59:10.337 [main] INFO  o.s.b.f.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [activiti.cfg.xml]
17:59:12.460 [main] INFO  o.a.engine.impl.db.DbSqlSession - performing create on engine with resource org/activiti/db/create/activiti.h2.create.engine.sql
17:59:12.521 [main] INFO  o.a.engine.impl.db.DbSqlSession - performing create on history with resource org/activiti/db/create/activiti.h2.create.history.sql
17:59:12.536 [main] INFO  o.a.engine.impl.db.DbSqlSession - performing create on identity with resource org/activiti/db/create/activiti.h2.create.identity.sql
17:59:12.542 [main] INFO  o.a.engine.impl.ProcessEngineImpl - ProcessEngine default created
17:59:12.544 [main] INFO  o.a.e.impl.jobexecutor.JobExecutor - Starting up the JobExecutor[org.activiti.engine.impl.jobexecutor.DefaultJobExecutor].
17:59:12.546 [Thread-3] INFO  o.a.e.i.j.AcquireJobsRunnable - JobExecutor[org.activiti.engine.impl.jobexecutor.DefaultJobExecutor] starting to acquire jobs
17:59:12.589 [main] INFO  o.a.e.i.bpmn.deployer.BpmnDeployer - Processing resource activiti-processes/ParallelServiceTest.bpmn20.xml
17:59:12.765 [main] INFO  o.a.e.i.bpmn.deployer.BpmnDeployer - Processing resource activiti-processes/GridEnvironment.bpmn20.xml
17:59:12.875 [main] INFO  o.a.e.i.bpmn.deployer.BpmnDeployer - Processing resource activiti-processes/ResultAndExceptionTest.bpmn20.xml

0 - waiting 3180ms
1 - waiting 16960ms
2 - waiting 540ms
3 - waiting 700ms
4 - waiting 4100ms
5 - waiting 15320ms
6 - waiting 14640ms
7 - waiting 800ms
8 - waiting 10980ms
9 - waiting 320ms
</code>

trademak
Star Contributor
Star Contributor
You're right, multi instance doesn't work together with async. Is there a specific use case you would like to implement using a similar construct? Could you explain that use case? It could be that Activiti isn't suited for this use case.

Best regards,