Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static void bulkDeleteHistoricCaseInstances(Collection<String> caseInstan

if (cmmnEngineConfiguration.isEnableEntityLinks()) {
cmmnEngineConfiguration.getEntityLinkServiceConfiguration().getHistoricEntityLinkService()
.bulkDeleteHistoricEntityLinksForScopeTypeAndScopeIds(ScopeTypes.CMMN, caseInstanceIds);
.bulkDeleteHistoricEntityLinksForScopeTypeAndScopeIdsOrReferenceScopeIds(ScopeTypes.CMMN, caseInstanceIds);
}

HistoricVariableInstanceEntityManager historicVariableInstanceEntityManager = cmmnEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableInstanceEntityManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public void recordHistoricCaseInstanceDeleted(String caseInstanceId, String tena

if (cmmnEngineConfiguration.isEnableEntityLinks()) {
cmmnEngineConfiguration.getEntityLinkServiceConfiguration().getHistoricEntityLinkService()
.deleteHistoricEntityLinksByScopeIdAndScopeType(historicCaseInstance.getId(), ScopeTypes.CMMN);
.deleteHistoricEntityLinksByScopeIdOrReferenceScopeIdAndScopeType(historicCaseInstance.getId(), ScopeTypes.CMMN);
}

HistoricVariableInstanceEntityManager historicVariableInstanceEntityManager = cmmnEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableInstanceEntityManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,11 @@ public static void deleteHistoricTask(String taskId, CmmnEngineConfiguration cmm

cmmnEngineConfiguration.getIdentityLinkServiceConfiguration().getHistoricIdentityLinkService().deleteHistoricIdentityLinksByTaskId(taskId);

if (cmmnEngineConfiguration.isEnableEntityLinks()) {
cmmnEngineConfiguration.getEntityLinkServiceConfiguration().getHistoricEntityLinkService()
.deleteHistoricEntityLinksByScopeIdOrReferenceScopeIdAndScopeType(taskId, ScopeTypes.TASK);
}

historicTaskService.deleteHistoricTask(historicTaskInstance);
}
}
Expand Down Expand Up @@ -283,6 +288,11 @@ protected static void bulkDeleteHistoricTaskInstances(Collection<String> taskIds
cmmnEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableService().bulkDeleteHistoricVariableInstancesByTaskIds(taskIds);
cmmnEngineConfiguration.getIdentityLinkServiceConfiguration().getHistoricIdentityLinkService().bulkDeleteHistoricIdentityLinksForTaskIds(taskIds);

if (cmmnEngineConfiguration.isEnableEntityLinks()) {
cmmnEngineConfiguration.getEntityLinkServiceConfiguration().getHistoricEntityLinkService()
.bulkDeleteHistoricEntityLinksForScopeTypeAndScopeIdsOrReferenceScopeIds(ScopeTypes.TASK, taskIds);
}

historicTaskService.bulkDeleteHistoricTaskInstances(taskIds);

historicTaskService.bulkDeleteHistoricTaskLogEntriesForTaskIds(taskIds);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import java.util.List;

import org.flowable.cmmn.api.runtime.CaseInstance;
import org.flowable.cmmn.api.runtime.PlanItemInstance;
import org.flowable.cmmn.api.runtime.PlanItemInstanceState;
import org.flowable.cmmn.engine.impl.util.CommandContextUtil;
import org.flowable.cmmn.engine.test.CmmnDeployment;
import org.flowable.cmmn.test.FlowableCmmnTestCase;
Expand Down Expand Up @@ -328,6 +330,93 @@ public ByteArrayEntity execute(CommandContext commandContext) {
}
}

@Test
@CmmnDeployment(resources = "org/flowable/cmmn/test/one-human-task-model.cmmn")
public void deleteHistoricTaskInstanceRemovesReferenceScopeOrphans() {
HistoryLevel historyLevel = cmmnEngineConfiguration.getHistoryLevel();
try {
cmmnEngineConfiguration.setHistoryLevel(HistoryLevel.FULL);
CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder()
.caseDefinitionKey("oneTaskCase")
.start();

Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).singleResult();

waitForAsyncHistoryExecutorToProcessAllJobs();

// The case-task entity link points at the task as the REFERENCE_SCOPE_ID_.
assertThat(cmmnHistoryService.getHistoricEntityLinkParentsForTask(task.getId())).isNotEmpty();

cmmnTaskService.complete(task.getId());
waitForAsyncHistoryExecutorToProcessAllJobs();

cmmnHistoryService.deleteHistoricTaskInstance(task.getId());

assertThat(cmmnHistoryService.getHistoricEntityLinkParentsForTask(task.getId())).isEmpty();
assertThat(cmmnHistoryService.getHistoricEntityLinkChildrenForTask(task.getId())).isEmpty();

cmmnHistoryService.deleteHistoricCaseInstance(caseInstance.getId());
} finally {
cmmnEngineConfiguration.setHistoryLevel(historyLevel);
}
}

@Test
@CmmnDeployment(resources = {
"org/flowable/cmmn/test/runtime/CaseTaskTest.testBasicBlocking.cmmn",
"org/flowable/cmmn/test/runtime/oneTaskCase.cmmn"
})
public void deleteHistoricCaseInstanceRemovesReferenceScopeOrphans() {
HistoryLevel historyLevel = cmmnEngineConfiguration.getHistoryLevel();
try {
cmmnEngineConfiguration.setHistoryLevel(HistoryLevel.FULL);
CaseInstance parentCase = cmmnRuntimeService.createCaseInstanceBuilder()
.caseDefinitionKey("myCase")
.start();

// Trigger Task One so the entry sentry on the case task fires and the sub-case starts.
PlanItemInstance taskOnePlanItem = cmmnRuntimeService.createPlanItemInstanceQuery()
.caseInstanceId(parentCase.getId())
.planItemDefinitionId("task1")
.planItemInstanceState(PlanItemInstanceState.ACTIVE)
.singleResult();
cmmnRuntimeService.triggerPlanItemInstance(taskOnePlanItem.getId());

CaseInstance subCase = cmmnRuntimeService.createCaseInstanceQuery()
.caseDefinitionKey("oneTaskCase")
.singleResult();
assertThat(subCase).isNotNull();

PlanItemInstance subTaskPlanItem = cmmnRuntimeService.createPlanItemInstanceQuery()
.caseInstanceId(subCase.getId())
.planItemInstanceState(PlanItemInstanceState.ACTIVE)
.singleResult();
cmmnRuntimeService.triggerPlanItemInstance(subTaskPlanItem.getId());

waitForAsyncHistoryExecutorToProcessAllJobs();

// The sub-case is propagated as the REFERENCE_SCOPE_ID_ in the parent's child entity link.
assertThat(cmmnHistoryService.getHistoricEntityLinkParentsForCaseInstance(subCase.getId())).isNotEmpty();

cmmnHistoryService.deleteHistoricCaseInstance(subCase.getId());

assertThat(cmmnHistoryService.getHistoricEntityLinkParentsForCaseInstance(subCase.getId())).isEmpty();
assertThat(cmmnHistoryService.getHistoricEntityLinkChildrenForCaseInstance(subCase.getId())).isEmpty();

// Trigger Task Two so the parent case completes, then drop it from history.
PlanItemInstance taskTwoPlanItem = cmmnRuntimeService.createPlanItemInstanceQuery()
.caseInstanceId(parentCase.getId())
.planItemDefinitionId("task2")
.planItemInstanceState(PlanItemInstanceState.ACTIVE)
.singleResult();
cmmnRuntimeService.triggerPlanItemInstance(taskTwoPlanItem.getId());
waitForAsyncHistoryExecutorToProcessAllJobs();
cmmnHistoryService.deleteHistoricCaseInstance(parentCase.getId());
} finally {
cmmnEngineConfiguration.setHistoryLevel(historyLevel);
}
}

protected void validateEmptyHistoricDataForCaseInstance(String caseInstanceId) {
assertThat(cmmnHistoryService.createHistoricVariableInstanceQuery().caseInstanceId(caseInstanceId).list()).hasSize(0);
assertThat(cmmnHistoryService.getHistoricIdentityLinksForCaseInstance(caseInstanceId)).hasSize(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ public void recordProcessInstanceDeleted(String processInstanceId, String proces
processEngineConfiguration.getIdentityLinkServiceConfiguration().getHistoricIdentityLinkService().deleteHistoricIdentityLinksByProcessInstanceId(processInstanceId);

if (processEngineConfiguration.isEnableEntityLinks()) {
processEngineConfiguration.getEntityLinkServiceConfiguration().getHistoricEntityLinkService().deleteHistoricEntityLinksByScopeIdAndScopeType(processInstanceId, ScopeTypes.BPMN);
processEngineConfiguration.getEntityLinkServiceConfiguration().getHistoricEntityLinkService()
.deleteHistoricEntityLinksByScopeIdOrReferenceScopeIdAndScopeType(processInstanceId, ScopeTypes.BPMN);
}

getCommentEntityManager().deleteCommentsByProcessInstanceId(processInstanceId);
Expand Down Expand Up @@ -183,7 +184,8 @@ public void recordBulkDeleteProcessInstances(Collection<String> processInstanceI
processEngineConfiguration.getIdentityLinkServiceConfiguration().getHistoricIdentityLinkService().bulkDeleteHistoricIdentityLinksForProcessInstanceIds(processInstanceIds);

if (processEngineConfiguration.isEnableEntityLinks()) {
processEngineConfiguration.getEntityLinkServiceConfiguration().getHistoricEntityLinkService().bulkDeleteHistoricEntityLinksForScopeTypeAndScopeIds(ScopeTypes.BPMN, processInstanceIds);
processEngineConfiguration.getEntityLinkServiceConfiguration().getHistoricEntityLinkService()
.bulkDeleteHistoricEntityLinksForScopeTypeAndScopeIdsOrReferenceScopeIds(ScopeTypes.BPMN, processInstanceIds);
}

getCommentEntityManager().bulkDeleteCommentsForProcessInstanceIds(processInstanceIds);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,11 @@ public static void deleteHistoricTask(String taskId) {
}
processEngineConfiguration.getIdentityLinkServiceConfiguration().getHistoricIdentityLinkService().deleteHistoricIdentityLinksByTaskId(taskId);

if (processEngineConfiguration.isEnableEntityLinks()) {
processEngineConfiguration.getEntityLinkServiceConfiguration().getHistoricEntityLinkService()
.deleteHistoricEntityLinksByScopeIdOrReferenceScopeIdAndScopeType(taskId, ScopeTypes.TASK);
}

historicTaskService.deleteHistoricTask(historicTaskInstance);
}
}
Expand Down Expand Up @@ -712,6 +717,11 @@ protected static void bulkDeleteHistoricTaskInstances(Collection<String> taskIds
processEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableService().bulkDeleteHistoricVariableInstancesByTaskIds(taskIds);
processEngineConfiguration.getIdentityLinkServiceConfiguration().getHistoricIdentityLinkService().bulkDeleteHistoricIdentityLinksForTaskIds(taskIds);

if (processEngineConfiguration.isEnableEntityLinks()) {
processEngineConfiguration.getEntityLinkServiceConfiguration().getHistoricEntityLinkService()
.bulkDeleteHistoricEntityLinksForScopeTypeAndScopeIdsOrReferenceScopeIds(ScopeTypes.TASK, taskIds);
}

historicTaskService.bulkDeleteHistoricTaskInstances(taskIds);
historicTaskService.bulkDeleteHistoricTaskLogEntriesForTaskIds(taskIds);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,104 @@ public List<HistoricEntityLink> execute(CommandContext commandContext) {
}
}

@Test
@Deployment(resources = { "org/flowable/engine/test/history/callActivity.bpmn20.xml",
"org/flowable/engine/test/history/subProcess.bpmn20.xml" })
public void deleteHistoricTaskInstanceRemovesReferenceScopeOrphans() {
HistoryLevel historyLevel = processEngineConfiguration.getHistoryLevel();
try {
processEngineConfiguration.setHistoryLevel(HistoryLevel.FULL);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("callActivity");

Task mainTask = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
taskService.complete(mainTask.getId());

ProcessInstance subProcessInstance = runtimeService.createProcessInstanceQuery().superProcessInstanceId(processInstance.getId()).singleResult();
Task subTask = taskService.createTaskQuery().processInstanceId(subProcessInstance.getId()).singleResult();
String subTaskId = subTask.getId();

// Complete the sub-task so it has an endTime; deleteHistoricTaskInstance only works on completed tasks.
taskService.complete(subTaskId);
waitForHistoryJobExecutorToProcessAllJobs(10000, 400);

// The sub-process task carries a parent link (root scope = parent process instance) before delete.
assertThat(historyService.getHistoricEntityLinkParentsForTask(subTaskId)).isNotEmpty();

historyService.deleteHistoricTaskInstance(subTaskId);

// After deleting only the historic task, no row in ACT_HI_ENTITYLINK may still reference it.
assertThat(historyService.getHistoricEntityLinkParentsForTask(subTaskId)).isEmpty();
assertThat(historyService.getHistoricEntityLinkChildrenForTask(subTaskId)).isEmpty();

historyService.deleteHistoricProcessInstance(processInstance.getId());
} finally {
processEngineConfiguration.setHistoryLevel(historyLevel);
}
}

@Test
@Deployment(resources = { "org/flowable/engine/test/history/callActivity.bpmn20.xml",
"org/flowable/engine/test/history/subProcess.bpmn20.xml" })
public void deleteHistoricProcessInstanceRemovesReferenceScopeOrphans() {
HistoryLevel historyLevel = processEngineConfiguration.getHistoryLevel();
try {
processEngineConfiguration.setHistoryLevel(HistoryLevel.FULL);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("callActivity");

Task mainTask = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
taskService.complete(mainTask.getId());

ProcessInstance subProcessInstance = runtimeService.createProcessInstanceQuery().superProcessInstanceId(processInstance.getId()).singleResult();
Task subTask = taskService.createTaskQuery().processInstanceId(subProcessInstance.getId()).singleResult();
taskService.complete(subTask.getId());

waitForHistoryJobExecutorToProcessAllJobs(10000, 400);

// The sub-process is propagated as the REFERENCE_SCOPE_ID_ in the parent's child entity link.
assertThat(historyService.getHistoricEntityLinkParentsForProcessInstance(subProcessInstance.getId())).isNotEmpty();

historyService.deleteHistoricProcessInstance(subProcessInstance.getId());

assertThat(historyService.getHistoricEntityLinkParentsForProcessInstance(subProcessInstance.getId())).isEmpty();
assertThat(historyService.getHistoricEntityLinkChildrenForProcessInstance(subProcessInstance.getId())).isEmpty();

historyService.deleteHistoricProcessInstance(processInstance.getId());
} finally {
processEngineConfiguration.setHistoryLevel(historyLevel);
}
}

@Test
@Deployment(resources = { "org/flowable/engine/test/history/callActivity.bpmn20.xml",
"org/flowable/engine/test/history/subProcess.bpmn20.xml" })
public void bulkDeleteHistoricProcessInstancesRemovesReferenceScopeOrphans() {
HistoryLevel historyLevel = processEngineConfiguration.getHistoryLevel();
try {
processEngineConfiguration.setHistoryLevel(HistoryLevel.FULL);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("callActivity");

Task mainTask = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
taskService.complete(mainTask.getId());

ProcessInstance subProcessInstance = runtimeService.createProcessInstanceQuery().superProcessInstanceId(processInstance.getId()).singleResult();
Task subTask = taskService.createTaskQuery().processInstanceId(subProcessInstance.getId()).singleResult();
taskService.complete(subTask.getId());

waitForHistoryJobExecutorToProcessAllJobs(10000, 400);

assertThat(historyService.getHistoricEntityLinkParentsForProcessInstance(subProcessInstance.getId())).isNotEmpty();

historyService.bulkDeleteHistoricProcessInstances(Collections.singletonList(subProcessInstance.getId()));

assertThat(historyService.getHistoricEntityLinkParentsForProcessInstance(subProcessInstance.getId())).isEmpty();
assertThat(historyService.getHistoricEntityLinkChildrenForProcessInstance(subProcessInstance.getId())).isEmpty();

historyService.deleteHistoricProcessInstance(processInstance.getId());
} finally {
processEngineConfiguration.setHistoryLevel(historyLevel);
}
}

protected void validateEmptyHistoricDataForProcessInstance(String processInstanceId) {
assertThat(historyService.createHistoricDetailQuery().processInstanceId(processInstanceId).list()).hasSize(0);
assertThat(historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list()).hasSize(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,22 @@ default List<HistoricEntityLink> findHistoricEntityLinksByScopeDefinitionIdAndSc

void deleteHistoricEntityLinksByScopeIdAndScopeType(String scopeId, String scopeType);

/**
* Delete every historic entity link where the given id appears as either the scope id
* (with the given scope type) or the reference scope id (with the given reference scope type).
* Used when an entity is removed from history to ensure no orphan rows are left pointing at it
* from either direction.
*/
void deleteHistoricEntityLinksByScopeIdOrReferenceScopeIdAndScopeType(String id, String scopeType);

void deleteHistoricEntityLinksByScopeDefinitionIdAndScopeType(String scopeDefinitionId, String scopeType);

void bulkDeleteHistoricEntityLinksForScopeTypeAndScopeIds(String scopeType, Collection<String> scopeIds);

/**
* Bulk variant of {@link #deleteHistoricEntityLinksByScopeIdOrReferenceScopeIdAndScopeType(String, String)}.
*/
void bulkDeleteHistoricEntityLinksForScopeTypeAndScopeIdsOrReferenceScopeIds(String scopeType, Collection<String> ids);

void deleteHistoricEntityLinksForNonExistingProcessInstances();

Expand Down
Loading