CaseInstanceMigrationBuilder

Hi,

I’m migrating case data from another system into our new system which is using Flowable as it’s workflow engine. As such, many of the cases are already in flight so I need to be able to start the case instances at an appropriate “later” stage rather than step 1.

In order to achieve this I’m starting the case instance at “step 1” and then using a case migration builder to specify appropriate ActivatePlanItemDefinition and TerminatePlanItemDefinition Mappings and migrating the case instance to “itself” (i.e. the same model version rather a new version)

This is actually working quite well, however, I’ve run into a situation where a processTask marked as async in one of the stages in my CMMN model which I’m trying to terminate is getting an OptimisticLockException and although the stage is terminated, the underlying process remains active.

I can see in the debug logs that the plan item instance is first being terminated and then later on in the logs it’s trying to update the plan item to active and hitting the exception.

From what I can see the initial case instance which I’ve started hasn’t quite finished starting before the migration builder tries to move it along and hits the exception. Putting in a thread.sleep(5000) before the migrate “fixes” the issue but I don’t think is ideal.

final CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder()			
				                      .caseDefinitionKey(definitionKeyId).start();

cmmnMigrationService.createCaseInstanceMigrationBuilder()
		.migrateToCaseDefinition(caseInstance.getCaseDefinitionId())                            
		.addTerminatePlanItemDefinitionMapping(...) 
		.addActivatePlanItemDefinitionMapping(...)                 
		.migrate(caseInstance.getId());	

I’ve also discovered that removing the async checkbox from the processtask also fixes the problem but at this point I’d prefer not to update the model

Do you have any other suggestions to make the migration play better with the starting instance other than a sleep(5000)? Anything with transaction configuration maybe?

Thanks,
Dan

Here’s the actual stack trace if it’s helpful

2023-06-29 10:51:40,732 DEBUG org.flowable.common.engine.impl.db.DbSqlSession 
 ? updating: PlanItemInstance with id: 0fa6aecf-1684-11ee-9961-0a0027000003, name: My Task, definitionId: MyTask, state: terminated 

...

2023-06-29 10:51:41,669 DEBUG org.flowable.common.engine.impl.db.DbSqlSession 
 ? updating: PlanItemInstance with id: 0fa6aecf-1684-11ee-9961-0a0027000003, name: My Task, definitionId: MyTask, state: active 
 
...

org.flowable.common.engine.api.FlowableOptimisticLockingException: PlanItemInstance with id: 0fa6aecf-1684-11ee-9961-0a0027000003, name: My Task, definitionId: MyTask, state: active was updated by another transaction concurrently
	at org.flowable.common.engine.impl.db.DbSqlSession.flushUpdates(DbSqlSession.java:577)
	at org.flowable.common.engine.impl.db.DbSqlSession.flush(DbSqlSession.java:364)
	at org.flowable.common.engine.impl.interceptor.CommandContext.flushSessions(CommandContext.java:212)
	at org.flowable.common.engine.impl.interceptor.CommandContext.close(CommandContext.java:70)
	at org.flowable.common.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:114)
	at org.flowable.common.spring.SpringTransactionInterceptor.lambda$execute$0(SpringTransactionInterceptor.java:57)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
	at org.flowable.common.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:57)
	at org.flowable.common.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:35)
	at org.flowable.common.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:56)
	at org.flowable.common.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:51)
	at org.flowable.job.service.impl.asyncexecutor.ExecuteAsyncRunnable.executeJob(ExecuteAsyncRunnable.java:127)
	at org.flowable.job.service.impl.asyncexecutor.ExecuteAsyncRunnable.run(ExecuteAsyncRunnable.java:115)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:829)

I should add that I’m using Flowable 6.7.2

Also just discovered that the cmmnRuntime has a ChangePlanItemStateBuilder that works much like the migration service but without the need to specify a definition. Wasn’t aware of that before. It gets the same optimistic lock exception unfortunately.

 cmmnRuntimeService.createChangePlanItemStateBuilder()
		.activatePlanItemDefinition(PlanItemDefinitionMappingBuilder.createActivatePlanItemDefinitionMappingFor("pendingStage"))
		.terminatePlanItemDefinitionId("processingStage")
		.terminatePlanItemDefinitionId("draftStage")
		.caseInstanceId(caseInstance.getId())
		.changeState();
```