Activiti / Activiti

Activiti is a light-weight workflow and Business Process Management (BPM) Platform targeted at business people, developers and system admins. Its core is a super-fast and rock-solid BPMN 2 process engine for Java. It's open-source and distributed under the Apache license. Activiti runs in any Java application, on a server, on a cluster or in the cloud. It integrates perfectly with Spring, it is extremely lightweight and based on simple concepts.
https://www.activiti.org
Apache License 2.0
10.11k stars 6.95k forks source link

Activiti >= 5.17 Referential integrity constraint violation: “ACT_FK_VAR_EXE” when adding local task variable prior completing task #3532

Open pleft opened 3 years ago

pleft commented 3 years ago

Please consider the following diagram

enter image description here

MyProcess.bpmn

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
  <process id="myProcess" name="My process" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask id="evl" name="Evaluation"></userTask>
    <boundaryEvent id="timer_event_autocomplete" name="Timer" attachedToRef="evl" cancelActivity="false">
      <timerEventDefinition>
        <timeDate>PT2S</timeDate>
      </timerEventDefinition>
    </boundaryEvent>
    <serviceTask id="timer_service" name="Timed Autocomplete" activiti:async="true" activiti:class="com.example.service.TimerService"></serviceTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="evl"></sequenceFlow>
    <sequenceFlow id="flow2" sourceRef="timer_event_autocomplete" targetRef="timer_service"></sequenceFlow>
    <sequenceFlow id="flow3" sourceRef="evl" targetRef="endevent1"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
  </process>
</definitions>

To describe it in words, there is one user task (Evaluation) and a timer attached to it (configured to trigger in 2 seconds). Upon triggering the timer, the Timed Autocomplete async service task in its Java Delegate, TimerService, tries to complete the user task (Evaluation) and the flow ends.

TimerService.java

public class TimerService implements JavaDelegate {
    Logger LOGGER = LoggerFactory.getLogger(TimerService.class);

    @Override
    public void execute(DelegateExecution execution) throws Exception {
        LOGGER.info("*** Executing Timer autocomplete ***");
        Task task = execution.getEngineServices().getTaskService().createTaskQuery().active().singleResult();
        execution.getEngineServices().getTaskService().setVariableLocal(task.getId(), "taskLocalVar", "task_local_var_value");
        execution.getEngineServices().getTaskService().complete(task.getId());
        LOGGER.info("*** Task: {} autocompleted by timer ***", task.getId());
    }
}

Prior completing the task please notice the line

execution.getEngineServices().getTaskService().setVariableLocal(task.getId(), "taskLocalVar", "task_local_var_value");

It just adds a local task variable. After this, the task gets completed by calling:

execution.getEngineServices().getTaskService().complete(task.getId());

This results to SQL exception to be thrown:

[pool-1-thread-2] ERROR org.activiti.engine.impl.interceptor.CommandContext  - Error while closing command context
org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database.  Cause: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "ACT_FK_VAR_EXE: PUBLIC.ACT_RU_VARIABLE FOREIGN KEY(EXECUTION_ID_) REFERENCES PUBLIC.ACT_RU_EXECUTION(ID_) ('7')"; SQL statement:
delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ? [23503-193]
### The error may involve org.activiti.engine.impl.persistence.entity.ExecutionEntity.deleteExecution-Inline
### The error occurred while setting parameters
### SQL: delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ?
### Cause: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "ACT_FK_VAR_EXE: PUBLIC.ACT_RU_VARIABLE FOREIGN KEY(EXECUTION_ID_) REFERENCES PUBLIC.ACT_RU_EXECUTION(ID_) ('7')"; SQL statement:
delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ? [23503-193]
    at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:172)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.delete(DefaultSqlSession.java:185)
    at org.activiti.engine.impl.db.DbSqlSession$CheckedDeleteOperation.execute(DbSqlSession.java:293)
    at org.activiti.engine.impl.db.DbSqlSession.flushRegularDeletes(DbSqlSession.java:897)
    at org.activiti.engine.impl.db.DbSqlSession.flushDeletes(DbSqlSession.java:890)
    at org.activiti.engine.impl.db.DbSqlSession.flush(DbSqlSession.java:617)
    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.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.jobexecutor.ExecuteJobsRunnable.handleMultipleJobs(ExecuteJobsRunnable.java:94)
    at org.activiti.engine.impl.jobexecutor.ExecuteJobsRunnable.run(ExecuteJobsRunnable.java:49)
    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:834)
Caused by: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "ACT_FK_VAR_EXE: PUBLIC.ACT_RU_VARIABLE FOREIGN KEY(EXECUTION_ID_) REFERENCES PUBLIC.ACT_RU_EXECUTION(ID_) ('7')"; SQL statement:
delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ? [23503-193]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
    at org.h2.message.DbException.get(DbException.java:179)
    at org.h2.message.DbException.get(DbException.java:155)
    at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:426)
    at org.h2.constraint.ConstraintReferential.checkRowRefTable(ConstraintReferential.java:443)
    at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:318)
    at org.h2.table.Table.fireConstraints(Table.java:967)
    at org.h2.table.Table.fireAfterRow(Table.java:985)
    at org.h2.command.dml.Delete.update(Delete.java:101)
    at org.h2.command.CommandContainer.update(CommandContainer.java:98)
    at org.h2.command.Command.executeUpdate(Command.java:258)
    at org.h2.jdbc.JdbcPreparedStatement.execute(JdbcPreparedStatement.java:201)
    at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:45)
    at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:73)
    at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:49)
    at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:115)
    at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:75)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:170)
    ... 16 more
[pool-1-thread-2] ERROR org.activiti.engine.impl.jobexecutor.ExecuteJobsRunnable  - exception during job execution: 
### Error updating database.  Cause: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "ACT_FK_VAR_EXE: PUBLIC.ACT_RU_VARIABLE FOREIGN KEY(EXECUTION_ID_) REFERENCES PUBLIC.ACT_RU_EXECUTION(ID_) ('7')"; SQL statement:
delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ? [23503-193]
### The error may involve org.activiti.engine.impl.persistence.entity.ExecutionEntity.deleteExecution-Inline
### The error occurred while setting parameters
### SQL: delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ?
### Cause: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "ACT_FK_VAR_EXE: PUBLIC.ACT_RU_VARIABLE FOREIGN KEY(EXECUTION_ID_) REFERENCES PUBLIC.ACT_RU_EXECUTION(ID_) ('7')"; SQL statement:
delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ? [23503-193]
org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database.  Cause: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "ACT_FK_VAR_EXE: PUBLIC.ACT_RU_VARIABLE FOREIGN KEY(EXECUTION_ID_) REFERENCES PUBLIC.ACT_RU_EXECUTION(ID_) ('7')"; SQL statement:
delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ? [23503-193]
### The error may involve org.activiti.engine.impl.persistence.entity.ExecutionEntity.deleteExecution-Inline
### The error occurred while setting parameters
### SQL: delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ?
### Cause: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "ACT_FK_VAR_EXE: PUBLIC.ACT_RU_VARIABLE FOREIGN KEY(EXECUTION_ID_) REFERENCES PUBLIC.ACT_RU_EXECUTION(ID_) ('7')"; SQL statement:
delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ? [23503-193]
    at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:172)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.delete(DefaultSqlSession.java:185)
    at org.activiti.engine.impl.db.DbSqlSession$CheckedDeleteOperation.execute(DbSqlSession.java:293)
    at org.activiti.engine.impl.db.DbSqlSession.flushRegularDeletes(DbSqlSession.java:897)
    at org.activiti.engine.impl.db.DbSqlSession.flushDeletes(DbSqlSession.java:890)
    at org.activiti.engine.impl.db.DbSqlSession.flush(DbSqlSession.java:617)
    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.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.jobexecutor.ExecuteJobsRunnable.handleMultipleJobs(ExecuteJobsRunnable.java:94)
    at org.activiti.engine.impl.jobexecutor.ExecuteJobsRunnable.run(ExecuteJobsRunnable.java:49)
    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:834)
Caused by: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "ACT_FK_VAR_EXE: PUBLIC.ACT_RU_VARIABLE FOREIGN KEY(EXECUTION_ID_) REFERENCES PUBLIC.ACT_RU_EXECUTION(ID_) ('7')"; SQL statement:
delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ? [23503-193]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
    at org.h2.message.DbException.get(DbException.java:179)
    at org.h2.message.DbException.get(DbException.java:155)
    at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:426)
    at org.h2.constraint.ConstraintReferential.checkRowRefTable(ConstraintReferential.java:443)
    at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:318)
    at org.h2.table.Table.fireConstraints(Table.java:967)
    at org.h2.table.Table.fireAfterRow(Table.java:985)
    at org.h2.command.dml.Delete.update(Delete.java:101)
    at org.h2.command.CommandContainer.update(CommandContainer.java:98)
    at org.h2.command.Command.executeUpdate(Command.java:258)
    at org.h2.jdbc.JdbcPreparedStatement.execute(JdbcPreparedStatement.java:201)
    at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:45)
    at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:73)
    at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:49)
    at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:115)
    at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:75)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:170)
    ... 16 more

Similar exception arises if we change the task variable not being local e.g.:

execution.getEngineServices().getTaskService().setVariable(task.getId(), "taskNonLocalVar", "task_non_local_var_value");

The same code executes successfully with activiti version 5.15.

A sample project can be found at: https://github.com/pleft/DemoActiviti just checkout the constraint_violation branch. It uses activiti version 5.22.0

What's the reason behind this exception? Shouldn't we set this way task variables (from the timer execution)? What has changed from 5.15 to 5.17 and causes this behavior?

Many thanks for your time.

pleft commented 3 years ago

It seems that in case we are inside a method that uses as parameter DelegateExecution (e.g. a JavaDelegate class or an activiti:expression method) which derives from a concurrent execution like the one from the timer in the example above, we should only use the DelegateExecution object to set variables, any call to RuntimeService.setVariableLocal or TaskService.setVariableLocal will cause the integrity constraint exception upon task.complete is called.

In case of setting a task variable then it should be done in a TaskListener.notify method where DelegateTask object is available and set the variables on it, e.g. delegateTask.setVariableLocal("taskLocalVar", "taskLocalVarValue");

Can somebody elaborate on the above? Why and how this changed from version 5.15? Where in the guide are these restrictions outlined?

mteodori commented 3 years ago

can you please create a unit test that reproduces the issue with Activiti 7? former versions are in maintenance mode and we only accept PRs

temiotu commented 3 years ago

What is the suggested way to add variables before completing a user task as any call to any call to RuntimeService.setVariableLocal or TaskService.setVariableLocal will cause the integrity constraint exception when task.complete is called.

hanlinshuai commented 10 months ago

可以尝试一下修改外键约束为 cascade