Flowable process engine issues and pitfalls

  1. Process file name's prefix must match process id in process definition itself. Eg if process id is "compliance-deposit-process", process file name must be "compliance-deposit-process-xxx.bpmn.xml" To verify whether process has been deployed. Use processDefinitionQuery object

    repositoryService.createProcessDefinitionQuery().list()
  2. To start sub processses in parallel, use "multiInstanceLoopCharacteristics"

    <callActivity id="callnamescreeningprocess" name="call namescreening process" calledElement="namescreening-process"
    flowable:inheritVariables="true">
    <multiInstanceLoopCharacteristics isSequential="false" flowable:collection="${namesToScreen.result.namesToScreen}" flowable:elementVariable="nameVariableToScreen" >
    <!-- completionCondition></completionCondition -->
    </multiInstanceLoopCharacteristics>
    </callActivity>
  3. An exception stacktrace appears in log when parallel call activity task joins. Yet, this error does not prevent process from continuing.

    Job 327 failed
    org.flowable.common.engine.api.FlowableOptimisticLockingException: VariableInstanceEntity[id=53, name=nrOfCompletedInstances, type=integer, longValue=1, textValue=1] was updated by another transaction concurrently
    at org.flowable.common.engine.impl.db.DbSqlSession.flushUpdates(DbSqlSession.java:505) ~[flowable-engine-common-6.4.1.jar:6.4.1]
    at org.flowable.common.engine.impl.db.DbSqlSession.flush(DbSqlSession.java:292) ~[flowable-engine-common-6.4.1.jar:6.4.1]
    at org.flowable.common.engine.impl.interceptor.CommandContext.flushSessions(CommandContext.java:191) ~[flowable-engine-common-6.4.1.jar:6.4.1]
    at org.flowable.common.engine.impl.interceptor.CommandContext.close(CommandContext.java:61) ~[flowable-engine-common-6.4.1.jar:6.4.1]
    at org.flowable.common.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:80) ~[flowable-engine-common-6.4.1.jar:6.4.1]
    at org.flowable.common.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:49) ~[flowable-spring-common-6.4.1.jar:6.4.1]
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.flowable.common.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:46) ~[flowable-spring-common-6.4.1.jar:6.4.1]
    at org.flowable.common.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:30) ~[flowable-engine-common-6.4.1.jar:6.4.1]
    at org.flowable.common.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:56) ~[flowable-engine-common-6.4.1.jar:6.4.1]
    at org.flowable.common.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:51) ~[flowable-engine-common-6.4.1.jar:6.4.1]
    at org.flowable.job.service.impl.asyncexecutor.ExecuteAsyncRunnable.executeJob(ExecuteAsyncRunnable.java:128) [flowable-job-service-6.4.1.jar:6.4.1]
    at org.flowable.job.service.impl.asyncexecutor.ExecuteAsyncRunnable.run(ExecuteAsyncRunnable.java:116) [flowable-job-service-6.4.1.jar:6.4.1]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_191]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_191]
    at java.lang.Thread.run(Thread.java:748) [?:1.8.0_191]
  4. Signal notification needs to be in a separate thread from the thread that start the process instance. This problem is especially salient in unit test case. Otherwise the signal will reach ealier whereas the process did not yet reach the corresponding "intermediateCatchEvent" node.

    val processInstanceId = executionService.startProcess("compliance-deposit-process", "100", listOf(ProcessVariable("deposit_service_host", "localhost:1010"),
    ))
    Thread {
    Thread.sleep(5000)
    executionService.continueProcess(
         processInstanceId, "event-wait-rule-index-completed", listOf(
         ProcessVariable("caseAttributes", caseCreationData))
    )
    }.start()
  5. Parallel gateway join pitfall:
    The initial process definition starts with an parallel gateway with two parallel execution path for transaction monitor and name screening. The transaction monitor execution starts an exclusive gateway with two logic branches, and all branches direct connect to the joining parallel gateway. However, the joining parallel gateway never exit.
    Flowable process engine issues and pitfalls
    Note that there are 3 inbound connection for the joining parallel gateway whereas only two of them will actually execute. This causes parallel gateway exit condition check failure. See org.flowable.engine.impl.bpmn.behavior.ParallelGatewayActivityBehavior:
// Fork

// Is needed to set the endTime for all historic activity joins
CommandContextUtil.getActivityInstanceEntityManager().recordActivityEnd((ExecutionEntity) execution, null);

if (nbrOfExecutionsCurrentlyJoined == nbrOfExecutionsToJoin) {

    // Fork
    if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("parallel gateway '{}' ({}) activates: {} of {} joined", execution.getCurrentActivityId(), 
                execution.getId(), nbrOfExecutionsCurrentlyJoined, nbrOfExecutionsToJoin);
    }
    ...
}

The workaround is to add a lightweight dummy script node to join the two logic branches from exclusive gateway first, then join the parallel gateway.

Flowable process engine issues and pitfalls

  1. Async task delay:
    From the log i found that there when the parent process starts multiple subprocesses, only 2 of them are executed at the same time. The engine triggers another batch after around 10 seconds. The key problem is that i inadventently add "flowable:async=true" to callActivity task. This seems to somehow reorder the task to execute and delay the invoation of subprocess for a while.

    <callActivity id="callnamescreeningprocess" name="call namescreening process" calledElement="namescreening-process"
    flowable:inheritVariables="true" flowable:async="true">

    Checking the code: org.flowable.job.service.impl.asyncexecutor.DefaultAsyncJobExecutor and org.flowable.job.service.impl.asyncexecutor.AcquireAsyncJobsDueRunnable: The aync job acquiring thread first checks remaining capacity in job pool (minimum of threadPoolQueue.remainingCapacity and MaxAsyncJobsDuePerAcquisition) and trying to poll a batch of jobs up to that size. If polled job size = remains capacity, this indicates that there are more job waiting to be executed. The job acquiring thread will not wait and immedidate starts another round of polling, otherwise, the thread pause for asyncExecutorDefaultAsyncJobAcquireWaitTime. Adjusting the variable "maxAsyncJobsDuePerAcquisition" (default value: 1) requires caution. Increase the value does increase throughput, but under cluster environment. It might cause more optimistic locking exception and degrade stability. Adjusting the variable "asyncExecutorDefaultAsyncJobAcquireWaitTime" to lower value (default value: 10000ms) also increase throughtput. But it might increase invalid db polling.

  2. Signal to continue subprocess:
    Sometimes, it is neccessary to continue a subprocess by signal from code. To do that, the processInstanceId of the subprocess must be known. Usually we will store parent processInstanceId in database table. But since the subprocesses are not triggered from code but via a multiInstance callActivity task in parent process definition, the processInstanceIds of the subprocesses are not known directly. A workaround to get the subprocess's processInstanceId is via ExecutionQuery.
val query = runtimeService.createExecutionQuery().activityId("event-wait-namescreening-result").variableValueEquals("nameVariableToScreen", "abc") 
val execution = query.list()[0] 
runtimeService.signalEventReceived(signalName, execution.id, variables.associate {
   Pair(it.name, it.value)
})

The "activityId" parameter takes value of an "intermediateCatchEvent" where the subprocesses are waiting for. This gives a list of subprocess executions that are waiting for the event signal. An additional variable to further distinguish which subprocess we want to signal, this should be the business key stored as a subprocess variable.

Problems:
multiInstanceLoopCharacteristics completionCondition expression: the expression evaluation context has only access to the multi instance execution's variable (nrOfActiveInstances, nrOfInstances, nrOfCompletedInstances) and its parent (main process). It cannot access variables to in the callActivity execution scope, this impose serious limitation on what we can do if we use multiple subprocesses for a vote scenario. Some workaround are discussed here: https://blog.csdn.net/weixin_40147618/article/details/83548270

猜你喜欢

转载自blog.51cto.com/shadowisper/2446686
今日推荐