cancel
Showing results for 
Search instead for 
Did you mean: 

How to handle or avoid an optimistic locking exception

schmke
Champ in-the-making
Champ in-the-making

I have an interesting scenario using 5.21 (moving to 5.22 and perhaps 6 soon) where I'm getting an ActivitiOptimisticLockingException. I'd like to figure out the best way to handle it or avoid it entirely.

I have a process that has a user task that on completion has Java service task that calls an API. The process then continues on to an end step. Pretty simple.

However, as a result of the API call, several other instances of the same process may be invalidated and I want to remove them. This gets triggered by the server the API call is made to sending out a JMS message, and the processing of the message looking up the related process instances by a specific variable/value. Presently, the process instance being completed shows up as a related process too (it has the same variable/value as the others).

This message and processing is all asynchronous to the process instance completion and happens quickly enough that the message processing tries to delete the process instance that the user completed the task for before the engine has fully finished the completion, and I get an ActivitiOptimisticLockingException, usually on the thread completing the task. Specifically:

org.activiti.engine.ActivitiOptimisticLockingException: HistoricVariableInstanceEntity[id=2647, name=euid2, revision=0, type=string, textValue=1000002002] was updated by another transaction concurrently
at org.activiti.engine.impl.db.DbSqlSession.flushUpdates(DbSqlSession.java:880)
at org.activiti.engine.impl.db.DbSqlSession.flush(DbSqlSession.java:619)
at org.activiti.engine.impl.interceptor.CommandContext.flushSessions(CommandContext.java:212)
at org.activiti.engine.impl.interceptor.CommandContext.close(CommandContext.java:138)
at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:66)
at org.activiti.engine.impl.interceptor.JtaTransactionInterceptor.execute(JtaTransactionInterceptor.java:65)
at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:31)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:40)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:35)
at org.activiti.engine.impl.FormServiceImpl.submitTaskFormData(FormServiceImpl.java:70)

Now, this occurs late enough in the task completion that the task itself is actually completed, so I suspect it is just historical variables that may not be fully populated correctly, but I'd still like to avoid it happening.

So, is there any way to avoid a simultaneous process instance deletion from affecting the processing of the same process instance being completed? Does 5.22 have anything to help with this? Will version 6 change this behavior at all? Or do I need to build something into my process to detect and avoid this? Or are there any engine settings that would alter this behavior?

One thought I had was in the message processing doing the delete, to look for any tasks for the process instance and if there are none, that would indicate the process instance is the one we want to let complete. The others we want to delete would still be at the user task.

Or do I need to set a process variable before the API call in the process that identifies that process instance as the one to not delete and use it when looking up the related process instances?

Thanks for any insight or suggestions.

5 REPLIES 5

jearles
Star Contributor
Star Contributor

Schmke,

You've been heard, and actually there's another post about something similar that we're looking into. I need to look into this a bit further; if you have a minimalistic reproduction, that would help a lot.

In 2013, one of the Activiti gurus commented on this same issue saying that: "This exception will only happen if you have multiple Activiti Engines with the job executor activated." Can you verify that this isn't the case?

Thanks,
-JEarles
bp3

schmke
Champ in-the-making
Champ in-the-making

Correct, there is just one Activiti engine running.

I don't think the other scenario is related, mine is just one where a Java service task triggers something external to delete a process instance that happens to be the process instance the Java service task ran as, and it all happens quickly enough that the engine isn't done finishing up the process instance completion when the delete happens.  I sometimes get the exception in the delete and sometimes back to the client that completed the task.

Is there any way for the asynchronous delete to check/know that the process instance is "in process" other than getting the exception?  If the exception only occurred in the client doing the delete, that would be fine, but it potentially happening on the process engine where the task is being completed and that seems like it would cause problems.

How you can verify "have multiple Activiti Engines with the job executor activated" ? Thanks! 

gdharley
Elite Collaborator
Elite Collaborator

As far as I know Version 6 will not resolve this issue as it really isnt a defect.

The current instance IS a related instance and therefore is being returned correctly.

You can either exclude it from the set of instances being deleted, perhaps by including the current instance ID in the message and excluding it on your message handler, or you could change the order of processing, perhaps by delaying the firing of the message until after the current instance gets to a wait state.

Hope this gives you some ideas,

Greg

 

schmke
Champ in-the-making
Champ in-the-making

Thanks for the reply.

The delete is presently asynchronous and doesn't know anything about the current instance ID unfortunately.  Ideally the engine would provide a way for the client doing the delete to know the instance is "in process" but I haven't determined a way to do that yet.  I considered doing a query to see if the instance has tasks (there should be none after the task is completed) but it appears that hasn't been persisted yet when the delete happens.

I could add a delay before trying the delete, but that feels like a hack and how long a delay is long enough?  I'm sure it would depend on the steps in the process after the Java service task that triggers the message causing the external delete.

I could set a variable right after task completion to allow the delete to filter this specific one out, but if the instance is still being processed, will that variable be persisted yet and seen to be filtered on?