cancel
Showing results for 
Search instead for 
Did you mean: 

Activiti parallel execution of subProcesses

semenchikus
Champ on-the-rise
Champ on-the-rise

I'm trying to implement the following workflow:

there are 3 separate subProcesses (dbTaskPrescoringProcess, level1PrescoringProcess and level2PrescoringProcess) that should be executed in concurrently. So I've added the following properties to each process: activiti:async="true" activiti:exclusive="false"

A service task ("Level2 Prescore") in level2PrescoringProcess should be executed by one of the following events:

- Timer event

- Signal event (throws in level1PrescoringProcess).

So Event-Based Gateway is used.

Of course I've added parallelGateways to the input and output to these subProcesses.

In the result I have two problems:

1. Level2 Prescore service task of level2PrescoringProcess starts with significant delay (more than timer event value, tried even PT1S).

2. dbTaskPrescoringProcess executes twice. Sometimes even after the parent process has been finished ("Print result" service task has been completed).

I am beginner in Activiti so please advice what I did wrong?

Thank you,

Simon

1 ACCEPTED ANSWER

daisuke-yoshimo
Star Collaborator
Star Collaborator

Well, it is not a very good method, but it may be solved by raising the revision of the exclusive control target entity.

For example,in DbTaskPrescoringDelegate.class, you should add the following code.
But, if you use the same variable between parallel executions, you should raise the VariableInstanceEntity and HistoricVariableInstanceEntity too.

ExecutionEntity execution = Context.getCommandContext().getDbSqlSession().findInCache(ExecutionEntity.class, delegateExecution.getId());
ExecutionEntity rootExecution = execution.getParent().getParent();

rootExecution.setRevision(rootExecution.getRevisionNext());
Context.getCommandContext().getDbSqlSession().update(rootExecution);

View answer in original post

9 REPLIES 9

daisuke-yoshimo
Star Collaborator
Star Collaborator

I customized your BPMN and tried it in Activiti 5.22.0 , but I could not reproduce it.

The problems may depend on your user program.

> the result I have two problems:

> 1. Level2 Prescore service task of level2PrescoringProcess starts with significant delay (more than timer event value, tried even PT1S).

> 2. dbTaskPrescoringProcess executes twice. Sometimes even after the parent process has been finished ("Print result" service task has been completed).

Is there a stack trace at the time of the above problem occurrence?

Thank you very much for your reply!

I'm not sure how to make a stack trace at the time of the problem occurrence, so I can show the source code of my test client application (client app) and 2 cases of running. In the Prescore service tasks I'm just emulating some long-running processing using cycle.

1. I've started process by REST request (StartProcessController). Both subProcesses (dbTaskPrescoringProcess and level2PrescoringProcess) start normally, so Level2Prescore service task should start approximately in 1 second (at 10:52:55) by the timer event, but it starts in 10 seconds after subProcess startup (Level2 Prescoring started at 10:53:04.529).

Process has started at 10:52:54.194
DB Prescoring started at 10:52:54.256...
onLevel2Prescoring at 10:52:54.258
DB Prescoring finised at 10:53:04.264.
Level2 Prescoring started at 10:53:04.529...
Level2 Prescoring finised at 10:53:04.531. 

1. Similar to the first example, but here DB prescoring service task executes 2 times without any reasons.

Process has started at 10:58:25.468
DB Prescoring started at 10:58:25.496...
onLevel2Prescoring at 10:58:25.518
Level2 Prescoring started at 10:58:34.629...
Level2 Prescoring finised at 10:58:34.630.
DB Prescoring finised at 10:58:35.510.
DB Prescoring started at 10:58:54.632...
DB Prescoring finised at 10:59:04.635.

Hopefully, it's my bad, because I don't understand why these issues apprear.

Best regards,

Simon

You can solve this problem by enabling exclusion of subprocess like the following.

activiti:exclusive="true"

Activiti engine performs optimistic locking of process instance entity.
So,dbTaskPrescoringProcess and level2PrescoringProcess work concurrently in each asynchronous thread and first sub process can commit and second sub process fail to commit by optimistic locking.

The JobExecutor of Activiti engine re-execute the failed job.
So, dbTaskPrescoringProcess is executed twice.

I checked this exeception message in the table act_ru_job.

ProcessInstance[5] was updated by another transaction concurrently

Reference
https://www.activiti.org/userguide/#exclusiveJobs

Right, making subProcesses exclusive will resolve the second problem. Unfortunately in this case dbTaskPrescoringProcess and level2PrescoringProcess cannot be executed at the same time, but I need to execute these subProcesses at the same time... I will try to move them into separate processes instead of subprocesses.

Thank you very much for your help!

hmm, looks like moving my subprocesses to separate processes will be quite tricky because I need to work with shared data between each process. Are there any possible ways to implement multiple parallel asynchronous tasks/subProcesses/processes/whatever that will be really concurrent and have shared data (like variables) feature?

daisuke-yoshimo
Star Collaborator
Star Collaborator

I'm sorry that finally I completely understood what you want to do.

You want to process them completely in parallel.

Yes, the task is really tricky Smiley Happy Still have no idea how to implement this design...

daisuke-yoshimo
Star Collaborator
Star Collaborator

Well, it is not a very good method, but it may be solved by raising the revision of the exclusive control target entity.

For example,in DbTaskPrescoringDelegate.class, you should add the following code.
But, if you use the same variable between parallel executions, you should raise the VariableInstanceEntity and HistoricVariableInstanceEntity too.

ExecutionEntity execution = Context.getCommandContext().getDbSqlSession().findInCache(ExecutionEntity.class, delegateExecution.getId());
ExecutionEntity rootExecution = execution.getParent().getParent();

rootExecution.setRevision(rootExecution.getRevisionNext());
Context.getCommandContext().getDbSqlSession().update(rootExecution);

This method doesn't seem safe enough to be used in production unfortunately. I'm thinking about start different processes for each business task and work with shared data using DB. Or even make custom solution using JMS instead of Activiti because the business tasks have strict timeout of execution (in milliseconds, <1s) and there is no Timer Catch Event with < 1 second waiting value in the Activiti...

Anyway, thank you very much for this solution! You are an expert in this framework.