We found an issue when trying to use @Transactional
along with a batch operation in Camunda. For a reason that we don’t understand, the Camunda operation is being launched too late, AFTER the code wrapped by the transaction is executed, as opposed to be executed inside that code in the place where it is invoked.
An entire a complete project where the issue can be reproduced is available in
camunda-example-spring-boot-transactional
In order to run the application, you just need to execute
mvn spring-boot:run
The problematic method is transactionBatch()
in the class CalculateBatchImpl.java. The method creates a fake list of data and calls the batch execution
logger.info("Create new Batch");
final List<String> simpleStringList = IntStream.range(0, 20)
.mapToObj(i -> "SomeRandomBatchData_" +
UUID.randomUUID()).collect(toList());
// Execute a custom camunda batch
Batch batch = CustomBatchBuilder.of(simpleStringList)
.configuration(this.processEngineFactoryBean.
getProcessEngineConfiguration())
.jobHandler(batchJobHandler).create();
Then, we implemented a mechanism to wait until the asynchronous process finishes, using an await library:
BatchQuery batchQuery =
processEngineFactoryBean.getProcessEngineConfiguration().
getManagementService()
.createBatchQuery().batchId(batch.getId());
// await to know when the camunda batch has finished
await().atMost(5, TimeUnit.MINUTES).until(
() -> 0 == batchQuery.list().size());
logger.info("Finish new Batch");
We put some log information inside the job handler to check when the Camunda batch is being executed. This code is inside the execute()
method of the handler:
@Override
public void execute(List<String> data, CommandContext commandContext) {
data.forEach(entry -> {
logger.info("Working on data: {}", entry);
runtimeService.startProcessInstanceByKey("loanApproval");
});
}
So, when executing the application, we expect to see in the log something like:
Create new Batch
Working on data: SomeRandomBatchData_0876151d-96dd-45ca-ae6e-3c2dbea8bfe0
Working on data: SomeRandomBatchData_3bcaf3a9-4e22-4894-be5b-afe3dd736944
Working on data: SomeRandomBatchData_6a3a1c55-da83-413b-b1fb-5130e2214c50
Finish new Batch
Unfortunately, what we see is as if the method finishes first and the Camunda batch operation happens after. Something like this:
Create new Batch
Finish new Batch <- This shouldn’t happen yet!!
Working on data: SomeRandomBatchData_0876151d-96dd-45ca-ae6e-3c2dbea8bfe0
Working on data: SomeRandomBatchData_3bcaf3a9-4e22-4894-be5b-afe3dd736944
Working on data: SomeRandomBatchData_6a3a1c55-da83-413b-b1fb-5130e2214c50
Actually we see that the Camunda operations happen after the run()
method in the main class.
If we remove the @Transactional
annotation, the application work as expected, executing the batch operation inside the scope of the method.
@Transactional
is mandatory for us because we need to do some local database operations AFTER the Camunda batch operation is finished and we need those operations to meet an ACID transaction along with the batch.
Thanks in advance for helping us to understand this behavior.
Juan.