OptimisticLockingException not resolved automatically via REST call

I have a simple process (attached) where two external tasks should be executed in parallel. To achieve that I set the first parallel gateway as asyncAfter / nonExclusive (to make tasks’ execution concurrent) and the second parallel gateway as asyncBefore (to create a save point after tasks are executed):


test.bpmn (7.2 KB)

If the tasks finish execution at exactly the same time (that can be simulated in the task’s code), I get an OptimisticLockingException from the engine. As far as I understand this behaviour is expected, but what I don’t understand is why the exception is not automatically resolved by the engine, but is reported as an incident instead!

I have found the following statement in the documentation:
Handling Optimistic Locking exceptions

In case the current Command is triggered by the Job Executor, OptimisticLockingException s are handled automatically using retries. Since this exception is expected to occur, it does not decrement the retry count.
If the current Command is triggered by an external API call, the Camunda Engine rolls back the current transaction to the last save point (wait state). Now the user has to decide how the exception should be handled, if the transaction should be retried or not.

I’m using REST API to trigger the process, so I believe that is the case why an incident is created. Is there a way to resolve the exception automatically (as they are stated to be “expected to occur”) so there is no user interaction required to resolve this exception as it is done when using an embedded Camunda process?

Hi @mkadan,

the Job Executor affects only Service tasks implemented by a Java Delegate or an expression. The retry handling in external tasks has to be done in your external task implementation. Have a look in the docs how to do it: https://docs.camunda.org/manual/develop/user-guide/process-engine/external-tasks/#reporting-task-failure.

To resolve OptimisticLockingExceptions you can the retryTimeout to 0 (That’s the internal implementation for JavaDelegates).

Hope this helps,

Hi @Ingo_Richtsmeier,

I think I did not describe the case correctly, I will try to explain it more in details.

We have several external task handlers that process tasks pulled from Camunda. As these tasks may take a significant time to execute, we decided to make the execution parallel. For that reason we added an outgoing parallel gateway (the first one on the diagram) with asyncAfter = true and exclusive = false.

This gateway leads to two external task handlers, and then to an incoming parallel gateway (the second one on the diagram) with asyncBefore = true. asyncBefore is set on the second gateway to create a transaction safe point so in a case of any exception external task handlers won’t execute the same task again.

In an artificial scenario we simulate these task handlers finish their work exactly at the same time. That leads to the second parallel gateway merging the outputs of both tasks concurrently.

If these tasks return a Map with the same key present:

{
“type”: “OptimisticLockingException”,
“message”: “ENGINE-03005 Execution of ‘UPDATE VariableInstanceEntity[015f53bb-a326-11e9-a031-acde48001122]’ failed. Entity was updated by another transaction concurrently.”
}

And if they return a Map with different keys instead:

{
“type”: “OptimisticLockingException”,
“message”: “ENGINE-03005 Execution of ‘UPDATE ExecutionEntity[89b275c1-a7c9-11e9-a909-acde48001122]’ failed. Entity was updated by another transaction concurrently.”
}

The workaround we use is to remove the return value from these tasks, so no concurrent merging is performed on the gateway side and therefore no OptimisticLockingException is thrown. But for a long-term solution we will need these return values back. So the question is if we can somehow setup value merging strategy to properly merge the values (e.g. using a custom implementation, or at least using LIFO) without throwing an OptimisticLockingException and thus blocking the process?

PS We have also tried setting retryTimeout = 0 for external tasks but that didn’t help, I believe cause the exception is not arising while processing the task, but later in the Camunda engine itself.!

Hi, i’ve met the same problem.
Is there any solution?