cancel
Showing results for 
Search instead for 
Did you mean: 

MultiThread - same variable created many times on the same process instance

qcap
Champ in-the-making
Champ in-the-making
Hello,

On our diagram, we use a ReceiveNode to wait all responses from outside to decide what to do next.

We use JMS to trigger the ReceiveNode, it do
- search Execution of  ReceiveNode,  we know Execution by querying on it's name.
- runtimeService.setVariable() on Execution. This variable is used to decide what to do next.
- runtimeService.signal(execution) to go ahead.

I can see that when we have the message JMS, the variable is set correctly with the right value and revision's number  ( 0 then 1 then 2 … ) when value changed.
But when have many JMS message on the same execution ( multithreading), I can see that activiti create the same variable with the same revision number. Until, it's not a problem.

But the process is never ended, it failed when try to  end the process, it try to clean all variables and save them on history,

### The error occurred while setting parameters
### SQL: delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`activiti`.`act_ru_variable`, CONSTRAINT `ACT_FK_VAR_EXE` FOREIGN KEY (`EXECUTION_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`))
   at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:23)

I think it caused by a double of variable on the same revision.

On the diagram, we use timer to end the process automatically, it failed becaused of above error and il rollback on the same node. So the same scenario is produced INFINITELY.

The cased is produced also on oracle/linux.

Thanks for your helps.








 












10 REPLIES 10

qcap
Champ in-the-making
Champ in-the-making
I have tested on activiti 5.11 & 5.14, same issue
Thanks

frederikherema1
Star Contributor
Star Contributor
We have optimistic locking in place for circumstances like these, although, that's only relevant if an exiting entity gets updated. Due to the fact that both threads do a simple setVariable(), 2 independent inserts will be done, not triggering any optimistic locking exceptions.

Since there are no constrains/indexes on the combination of variable name, rev, procId and taskId, both records will be present. This is inconvenient, although not super-critical (you'll always get one value back, depending on the order they come out of the result-set from jdbc).

The main culprit is the delete part. Variables are kept in a map (key=name) internally (inside the Execution/VariableSc - for performance reasons). We didn't anticipate multiple VariableInstances with the same name coming back from the DB, as they are always updated and updates are guarded with optimistic locking. When the execution is deleted, it will run over the VariableInstances in that map, and do a delete (is done this way to cleanup any byte arrays, which is not possible for a single-query delete). Offcourse, not deleting one of the two rows with the same variable name.

I'll look into this and see what we can do to fix this. We can at least prevent the delete from failing, by using the full VariableInstance list instead of using the map. For the insert part, this is a bit trickier…

qcap
Champ in-the-making
Champ in-the-making
Glad to hear, thanks

frederikherema1
Star Contributor
Star Contributor
A valid workaround would be to use the signal() method that takes in an execution-id an process-variables. This will ensure that the setting of the variable AND the signalling is done as a single operation. On top of that, it will force the second transaction to fail due to an optimistic locking exception in the execution itself, effectively rolling back any (duplicate) changes.

qcap
Champ in-the-making
Champ in-the-making
I've tested the do-it-all in one call, it is a GOOD workarroud
I have a fine exception
"org.activiti.engine.ActivitiOptimisticLockingException: ExecutionEntity[286f023a-7938-11e3-b92f-10ddb19c9a3a] was updated by another transaction concurrently"
And no more duplicate of variables, so it works on this case ( my case) precisely.
However, it could be till problematical on delete process when having duplicate variable ( behavior  desired ). Do you plan on next release ?

Thank you very much, nice job.










frederikherema1
Star Contributor
Star Contributor
Working on this now (http://jira.codehaus.org/browse/ACT-1887), will be part of the next release. I have a fix implemented for the setVariable and setTaskVariable methods on the API to do optimistic locking of the execution and task, respectively, so the duplicate inserts are impossible.

I'm also adding an additional fix for the duplicate variables (in case they were added in a pre 5.15 version) upon deletion. This will also be part of the 5.15 release. Thanks again for the clear bug-report!

jbarrez
Star Contributor
Star Contributor
FYI: the bugfix is now pushed to master. Indeed, thanks for the excellent bug report.

(and frederik for fixing it 😉

jcoveron
Champ in-the-making
Champ in-the-making
Greetings,

We are experiencing something similar as the issue described in this post using Activiti version 5.17.0. Let me explain the scenario first.

We have a workflow with two java service tasks between two parallel gateways (opening and closing). The service tasks are configured as activiti:async="true" and activiti:exclusive="false", while the parallel gateways are configured with activiti:async="true". The rest of the task as configured as activiti:exclusive="true"

The service tasks look something like this (simplified - see code below) and it starts a new thread with executes a http request and waits for its reply. Once the http reply is received, it signals the service task, so that the workflow can continue. Service task does not wait for the thread to finish. Meaning there is no thread.join(). Both service tasks execute the exact same code shown below.

<code>
public class CopyTask extends TaskActivityBehavior {

private Expression NicknameFrom;
private Expression NicknameTo;
private Expression FolderAddress;
private Expression FileNamePattern;

@Override
public void execute(ActivityExecution execution)  throws Exception {
   /*keep the current execution information that will be used to signal it later on*/
   String callbackId = execution.getId();
   String processInstanceId = execution.getProcessInstanceId();
   String currentActivitiId = execution.getCurrentActivityId();

   /*do some preparation, and start the new thread. Function does not wait for the thread to finish. Meaning there is no thread.join()*/
}

@Override
public void signal(ActivityExecution execution, String signalName, Object signalData) throws Exception
{
  leave(execution);
}
</code>


The thread that is executing the http request, creates a two variables that are passed to the signal function.

<code>
        private void signalWaitState(HashMap<String, String> Result) {
            ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
            RuntimeService runtimeService = processEngine.getRuntimeService();
           
            Map<String, Object> variableMap = new HashMap<String, Object>();
      variableMap.put("Code", Result.get("Code"));
      variableMap.put("Response", Result.get("Response"));
     
            runtimeService.signal(this.executionId, variableMap);
            System.out.println("Signalled execution id " + this.executionId + "(" + this.activityId + ")" + "(" + this.processInstanceId + ")");
        }
</code>

The whole process executes normally and without any problem. But when Activiti tries to do the clean up of act_ru_variable, we get the following error:
<code>
### The error occurred while setting parameters
### SQL: delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`activiti`.`act_ru_variable`, CONSTRAINT `ACT_FK_VAR_EXE` FOREIGN KEY (`EXECUTION_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`))
</code>

Due to this error, the process is rollbacked to the closing parallel gateway, but it does not move forward to the next step. As a result, the process never ends.

Activiti engine and rest are configured to run in the same server and accessing the same MySQL database. Both are configured with these options:
<code>
    <property name="jobExecutorActivate" value="false" />
    <property name="asyncExecutorEnabled" value="true" />
    <property name="asyncExecutorActivate" value="true" />
</code>

We ended up removing all the foreign key constrains on table act_ru_variable. Once removed, the process worked correctly.

So, we have some questions:
1) We read that this was fixed already, but we face this problem. Is there something we missed or need to configured?
2) What are the consequences of removing all the foreign key constrains on table act_ru_variable.

Let us know if you need additional information and many thanks in advance.

Keep on the good work on Activiti. It is a great tool.

trademak
Star Contributor
Star Contributor
Did you set the join parallel gateway to exclusive:true?
That's important, because that will make sure only 1 join thread will be processed at the same time.

There are no consequences of removing the foreign key constraints. It's just there for data consistency.

Best regards,