Optimistic locking exceptions without async

While doing performance tests on our application, we stumbled upon a problem related to Camunda. Correlating messages to a certain process leads to multiple OptimisticLockingExceptions. We solved this problem using application-level retries, but it still leaves the question why they are happening, as according to our understanding there shouldn’t be any issues. This is the process:

We correlate several (10-20) messages of type “Event 1” sequentially, waiting for each one to be successfully processes before correlating the next. However, the correlations happen very fast, with only tens of milliseconds between them. According to the guidelines, as there is no async before/after on the script tasks, after an Event 1 is correlated, these script tasks will run in the same transaction, the output variables will be set, and the process will end up listening again for Event 1. This appears to be the case (as no Event 1 messages get “lost”), but apparently something else is happening in the background, which causes subsequent correlations to fail with this exception:

org.camunda.bpm.engine.OptimisticLockingException: ENGINE-03005 Execution of 'UPDATE ExecutionEntity[75024616-0e4c-11ed-a5d8-e6cfe252a71f]' failed. Entity was updated by another transaction concurrently.
	at org.camunda.bpm.engine.impl.db.EnginePersistenceLogger.concurrentUpdateDbEntityException(EnginePersistenceLogger.java:136) ~[camunda-engine-7.16.0.jar:7.16.0]
	at org.camunda.bpm.engine.impl.db.entitymanager.DbEntityManager.handleConcurrentModification(DbEntityManager.java:413) ~[camunda-engine-7.16.0.jar:7.16.0]
	at org.camunda.bpm.engine.impl.db.entitymanager.DbEntityManager.flushDbOperations(DbEntityManager.java:356) ~[camunda-engine-7.16.0.jar:7.16.0]
	at org.camunda.bpm.engine.impl.db.entitymanager.DbEntityManager.flushDbOperationManager(DbEntityManager.java:323) ~[camunda-engine-7.16.0.jar:7.16.0]
	at org.camunda.bpm.engine.impl.db.entitymanager.DbEntityManager.flush(DbEntityManager.java:295) ~[camunda-engine-7.16.0.jar:7.16.0]
	at org.camunda.bpm.engine.impl.interceptor.CommandContext.flushSessions(CommandContext.java:272) ~[camunda-engine-7.16.0.jar:7.16.0]
	at org.camunda.bpm.engine.impl.interceptor.CommandContext.close(CommandContext.java:188) ~[camunda-engine-7.16.0.jar:7.16.0]
	at org.camunda.bpm.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:119) ~[camunda-engine-7.16.0.jar:7.16.0]
	at org.camunda.bpm.engine.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:72) ~[camunda-engine-spring-7.16.0.jar:7.16.0]
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar:5.3.20]
	at org.camunda.bpm.engine.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:70) ~[camunda-engine-spring-7.16.0.jar:7.16.0]
	at org.camunda.bpm.engine.impl.interceptor.ProcessApplicationContextInterceptor.execute(ProcessApplicationContextInterceptor.java:70) ~[camunda-engine-7.16.0.jar:7.16.0]
	at org.camunda.bpm.engine.impl.interceptor.CommandCounterInterceptor.execute(CommandCounterInterceptor.java:35) ~[camunda-engine-7.16.0.jar:7.16.0]
	at org.camunda.bpm.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:33) ~[camunda-engine-7.16.0.jar:7.16.0]
	at org.camunda.bpm.engine.impl.MessageCorrelationBuilderImpl.execute(MessageCorrelationBuilderImpl.java:322) ~[camunda-engine-7.16.0.jar:7.16.0]
	at org.camunda.bpm.engine.impl.MessageCorrelationBuilderImpl.correlateAllWithResult(MessageCorrelationBuilderImpl.java:271) ~[camunda-engine-7.16.0.jar:7.16.0]

The script tasks are relatively simple, they get variables, perform some logic, and return some output. Eg:

var type = execution.getVariable("type");
var cost = execution.getVariable('cost')

if (type != null) {
  "1";
} else if (cost != null) {
  "2";
} else {
  "3";
}

As for the environment, we’re running an embedded camunda in a spring boot application. The underlying database is PostgreSQL, with a default isolation level (read committed). We have also properly set up Camunda to use the application datasource/transaction manager.

process.bpmn (11.7 KB)