From c776dca44f7f0e7545cb832aa70fd8eb78784543 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 28 Aug 2026 12:48:57 +0200 Subject: [PATCH] Make historic variable cleanup dependent scope type aware Runtime cleanup honours the dependent scope types registered on an engine configuration, historic cleanup did not: such rows have no process instance id, and the CMMN historic delete pinned the scope type to 'cmmn', so they leaked along with their byte arrays. All dependent scope variables use the instance id as scope id, so the cleanup keys off that, which covers the whole BPMN execution tree in one statement. Adds a scope types aware find and (bulk) delete alongside the existing single scope type methods, used from DefaultHistoryManager, DefaultCmmnHistoryManager and CmmnHistoryHelper. Also fixes findHistoricalVariableInstancesBySubScopeIdAndScopeType, which passed a sub scope id to the scope id statement and matcher. --- .../impl/history/CmmnHistoryHelper.java | 2 +- .../history/DefaultCmmnHistoryManager.java | 2 +- .../history/BulkCaseInstanceDeleteTest.java | 89 +++++++++++++++++++ .../impl/history/DefaultHistoryManager.java | 16 +++- ...BulkDeleteHistoricProcessInstanceTest.java | 88 ++++++++++++++++++ .../service/HistoricVariableService.java | 4 + .../impl/HistoricVariableServiceImpl.java | 10 +++ ...HistoricVariableInstanceEntityManager.java | 6 ++ ...oricVariableInstanceEntityManagerImpl.java | 18 ++++ .../HistoricVariableInstanceDataManager.java | 4 + ...isHistoricVariableInstanceDataManager.java | 34 ++++++- ...InstanceByScopeIdAndScopeTypesMatcher.java | 35 ++++++++ .../entity/HistoricVariableInstance.xml | 49 ++++++++++ 13 files changed, 351 insertions(+), 6 deletions(-) create mode 100644 modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/data/impl/cachematcher/HistoricVariableInstanceByScopeIdAndScopeTypesMatcher.java diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/CmmnHistoryHelper.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/CmmnHistoryHelper.java index a89a240ea20..bece911daa4 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/CmmnHistoryHelper.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/CmmnHistoryHelper.java @@ -53,7 +53,7 @@ public static void bulkDeleteHistoricCaseInstances(Collection caseInstan } HistoricVariableInstanceEntityManager historicVariableInstanceEntityManager = cmmnEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableInstanceEntityManager(); - historicVariableInstanceEntityManager.bulkDeleteHistoricVariableInstancesByScopeIdsAndScopeType(caseInstanceIds, ScopeTypes.CMMN); + historicVariableInstanceEntityManager.bulkDeleteHistoricVariableInstancesByScopeIdsAndScopeTypes(caseInstanceIds, cmmnEngineConfiguration.getDependentScopeTypes()); TaskHelper.bulkDeleteHistoricTaskInstancesByCaseInstanceIds(caseInstanceIds, cmmnEngineConfiguration); diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/DefaultCmmnHistoryManager.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/DefaultCmmnHistoryManager.java index 39694e6de48..17c24193bdf 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/DefaultCmmnHistoryManager.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/DefaultCmmnHistoryManager.java @@ -218,7 +218,7 @@ public void recordHistoricCaseInstanceDeleted(String caseInstanceId, String tena HistoricVariableInstanceEntityManager historicVariableInstanceEntityManager = cmmnEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableInstanceEntityManager(); List historicVariableInstanceEntities = historicVariableInstanceEntityManager - .findHistoricalVariableInstancesByScopeIdAndScopeType(caseInstanceId, ScopeTypes.CMMN); + .findHistoricalVariableInstancesByScopeIdAndScopeTypes(caseInstanceId, cmmnEngineConfiguration.getDependentScopeTypes()); for (HistoricVariableInstanceEntity historicVariableInstanceEntity : historicVariableInstanceEntities) { historicVariableInstanceEntityManager.delete(historicVariableInstanceEntity); } diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/history/BulkCaseInstanceDeleteTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/history/BulkCaseInstanceDeleteTest.java index 0913788042d..4e9d205cb67 100644 --- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/history/BulkCaseInstanceDeleteTest.java +++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/history/BulkCaseInstanceDeleteTest.java @@ -15,8 +15,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import org.flowable.cmmn.api.runtime.CaseInstance; @@ -32,11 +34,16 @@ import org.flowable.common.engine.impl.persistence.entity.ByteArrayEntity; import org.flowable.identitylink.api.IdentityLinkType; import org.flowable.task.api.Task; +import org.flowable.variable.service.HistoricVariableService; +import org.flowable.variable.service.VariableServiceConfiguration; import org.flowable.variable.service.impl.persistence.entity.HistoricVariableInstanceEntity; +import org.flowable.variable.service.impl.types.SerializableType; import org.junit.jupiter.api.Test; public class BulkCaseInstanceDeleteTest extends FlowableCmmnTestCase { + protected static final String TEST_DEPENDENT_SCOPE_TYPE = "testDependentScope"; + @Test @CmmnDeployment(resources = "org/flowable/cmmn/test/one-human-task-model.cmmn") public void oneTaskTestWithVariables() { @@ -417,6 +424,88 @@ public void deleteHistoricCaseInstanceRemovesReferenceScopeOrphans() { } } + @Test + @CmmnDeployment(resources = "org/flowable/cmmn/test/one-human-task-model.cmmn") + public void deleteHistoricCaseInstanceRemovesDependentScopeVariables() { + cmmnEngineConfiguration.addDependentScopeType(TEST_DEPENDENT_SCOPE_TYPE); + try { + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("oneTaskCase").start(); + HistoricVariableInstanceEntity dependentVariable = createDependentScopeHistoricVariable(caseInstance.getId()); + String byteArrayId = dependentVariable.getByteArrayRef().getId(); + assertThat(byteArrayId).isNotNull(); + + Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).singleResult(); + cmmnTaskService.complete(task.getId()); + waitForAsyncHistoryExecutorToProcessAllJobs(); + + cmmnHistoryService.deleteHistoricCaseInstance(caseInstance.getId()); + + assertThat(findHistoricVariableInstanceById(dependentVariable.getId())).isNull(); + assertThat(findByteArrayById(byteArrayId)).isNull(); + } finally { + cmmnEngineConfiguration.getDependentScopeTypes().remove(TEST_DEPENDENT_SCOPE_TYPE); + } + } + + @Test + @CmmnDeployment(resources = "org/flowable/cmmn/test/one-human-task-model.cmmn") + public void bulkDeleteHistoricCaseInstancesRemovesDependentScopeVariables() { + cmmnEngineConfiguration.addDependentScopeType(TEST_DEPENDENT_SCOPE_TYPE); + try { + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("oneTaskCase").start(); + HistoricVariableInstanceEntity dependentVariable = createDependentScopeHistoricVariable(caseInstance.getId()); + String byteArrayId = dependentVariable.getByteArrayRef().getId(); + assertThat(byteArrayId).isNotNull(); + + Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).singleResult(); + cmmnTaskService.complete(task.getId()); + waitForAsyncHistoryExecutorToProcessAllJobs(); + + cmmnHistoryService.bulkDeleteHistoricCaseInstances(Collections.singletonList(caseInstance.getId())); + + assertThat(findHistoricVariableInstanceById(dependentVariable.getId())).isNull(); + assertThat(findByteArrayById(byteArrayId)).isNull(); + } finally { + cmmnEngineConfiguration.getDependentScopeTypes().remove(TEST_DEPENDENT_SCOPE_TYPE); + } + } + + /** + * Mimics a consumer that records history for its own dependent scope type: the row is linked to the case instance + * through the scope id, but carries a scope type the regular variable APIs never see. + */ + protected HistoricVariableInstanceEntity createDependentScopeHistoricVariable(String caseInstanceId) { + String planItemInstanceId = cmmnRuntimeService.createPlanItemInstanceQuery().caseInstanceId(caseInstanceId) + .planItemInstanceState(PlanItemInstanceState.ACTIVE).singleResult().getId(); + return cmmnEngineConfiguration.getCommandExecutor().execute(commandContext -> { + VariableServiceConfiguration variableServiceConfiguration = cmmnEngineConfiguration.getVariableServiceConfiguration(); + HistoricVariableService historicVariableService = variableServiceConfiguration.getHistoricVariableService(); + + HistoricVariableInstanceEntity historicVariable = historicVariableService.createHistoricVariableInstance(); + historicVariable.setName("dependentScopeVariable"); + historicVariable.setScopeId(caseInstanceId); + historicVariable.setSubScopeId(planItemInstanceId); + historicVariable.setScopeType(TEST_DEPENDENT_SCOPE_TYPE); + historicVariable.setVariableType(variableServiceConfiguration.getVariableTypes().getVariableType(SerializableType.TYPE_NAME)); + historicVariable.setCreateTime(new Date()); + historicVariable.setLastUpdatedTime(historicVariable.getCreateTime()); + historicVariable.setBytes("dependent scope value".getBytes(StandardCharsets.UTF_8)); + + historicVariableService.insertHistoricVariableInstance(historicVariable); + return historicVariable; + }); + } + + protected HistoricVariableInstanceEntity findHistoricVariableInstanceById(String id) { + return cmmnEngineConfiguration.getCommandExecutor().execute(commandContext -> cmmnEngineConfiguration + .getVariableServiceConfiguration().getHistoricVariableService().getHistoricVariableInstance(id)); + } + + protected ByteArrayEntity findByteArrayById(String id) { + return cmmnEngineConfiguration.getCommandExecutor().execute(commandContext -> CommandContextUtil + .getCmmnEngineConfiguration(commandContext).getByteArrayEntityManager().findById(id)); + } + protected void validateEmptyHistoricDataForCaseInstance(String caseInstanceId) { assertThat(cmmnHistoryService.createHistoricVariableInstanceQuery().caseInstanceId(caseInstanceId).list()).hasSize(0); assertThat(cmmnHistoryService.getHistoricIdentityLinksForCaseInstance(caseInstanceId)).hasSize(0); diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/DefaultHistoryManager.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/DefaultHistoryManager.java index eb37b1e2fea..e3690725e18 100644 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/DefaultHistoryManager.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/DefaultHistoryManager.java @@ -51,6 +51,7 @@ import org.flowable.task.service.impl.HistoricTaskInstanceQueryImpl; import org.flowable.task.service.impl.persistence.entity.HistoricTaskInstanceEntity; import org.flowable.task.service.impl.persistence.entity.TaskEntity; +import org.flowable.variable.service.HistoricVariableService; import org.flowable.variable.service.impl.persistence.entity.VariableInstanceEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -136,8 +137,12 @@ public void recordProcessInstanceDeleted(String processInstanceId, String proces getHistoricDetailEntityManager().deleteHistoricDetailsByProcessInstanceId(processInstanceId); if (getHistoryConfigurationSettings().isHistoryEnabledForVariables(processDefinitionId)) { - processEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableService() - .deleteHistoricVariableInstancesByProcessInstanceId(processInstanceId); + HistoricVariableService historicVariableService = processEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableService(); + historicVariableService.deleteHistoricVariableInstancesByProcessInstanceId(processInstanceId); + + // Variables stored under a dependent scope type have no process instance id, they are linked to the process instance through the scope id + historicVariableService.deleteHistoricVariableInstancesByScopeIdAndScopeTypes(processInstanceId, + processEngineConfiguration.getDependentScopeTypes()); } getHistoricActivityInstanceEntityManager().deleteHistoricActivityInstancesByProcessInstanceId(processInstanceId); TaskHelper.deleteHistoricTaskInstancesByProcessInstanceId(processInstanceId); @@ -178,7 +183,12 @@ public void recordDeleteHistoricProcessInstancesByProcessDefinitionId(String pro public void recordBulkDeleteProcessInstances(Collection processInstanceIds) { if (isHistoryEnabled() && processInstanceIds != null && !processInstanceIds.isEmpty()) { getHistoricDetailEntityManager().bulkDeleteHistoricDetailsByProcessInstanceIds(processInstanceIds); - processEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableService().bulkDeleteHistoricVariableInstancesByProcessInstanceIds(processInstanceIds); + HistoricVariableService historicVariableService = processEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableService(); + historicVariableService.bulkDeleteHistoricVariableInstancesByProcessInstanceIds(processInstanceIds); + + // Variables stored under a dependent scope type have no process instance id, they are linked to the process instance through the scope id + historicVariableService.bulkDeleteHistoricVariableInstancesByScopeIdsAndScopeTypes(processInstanceIds, + processEngineConfiguration.getDependentScopeTypes()); getHistoricActivityInstanceEntityManager().bulkDeleteHistoricActivityInstancesByProcessInstanceIds(processInstanceIds); TaskHelper.bulkDeleteHistoricTaskInstancesForProcessInstanceIds(processInstanceIds); processEngineConfiguration.getIdentityLinkServiceConfiguration().getHistoricIdentityLinkService().bulkDeleteHistoricIdentityLinksForProcessInstanceIds(processInstanceIds); diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/history/BulkDeleteHistoricProcessInstanceTest.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/history/BulkDeleteHistoricProcessInstanceTest.java index 5dd76ec9785..24faae3ca0f 100644 --- a/modules/flowable-engine/src/test/java/org/flowable/engine/test/history/BulkDeleteHistoricProcessInstanceTest.java +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/history/BulkDeleteHistoricProcessInstanceTest.java @@ -16,8 +16,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import org.flowable.common.engine.api.FlowableObjectNotFoundException; @@ -34,11 +36,16 @@ import org.flowable.entitylink.api.EntityLinkType; import org.flowable.entitylink.api.history.HistoricEntityLink; import org.flowable.task.api.Task; +import org.flowable.variable.service.HistoricVariableService; +import org.flowable.variable.service.VariableServiceConfiguration; import org.flowable.variable.service.impl.persistence.entity.HistoricVariableInstanceEntity; +import org.flowable.variable.service.impl.types.SerializableType; import org.junit.jupiter.api.Test; public class BulkDeleteHistoricProcessInstanceTest extends PluggableFlowableTestCase { + protected static final String TEST_DEPENDENT_SCOPE_TYPE = "testDependentScope"; + @Test @Deployment(resources = { "org/flowable/engine/test/bpmn/oneTask.bpmn20.xml" }) public void oneTaskTestWithVariables() { @@ -554,6 +561,87 @@ public void bulkDeleteHistoricProcessInstancesRemovesReferenceScopeOrphans() { } } + @Test + @Deployment(resources = { "org/flowable/engine/test/bpmn/oneTask.bpmn20.xml" }) + public void deleteHistoricProcessInstanceRemovesDependentScopeVariables() { + processEngineConfiguration.addDependentScopeType(TEST_DEPENDENT_SCOPE_TYPE); + try { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("startToEnd"); + HistoricVariableInstanceEntity dependentVariable = createDependentScopeHistoricVariable(processInstance.getId()); + String byteArrayId = dependentVariable.getByteArrayRef().getId(); + assertThat(byteArrayId).isNotNull(); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.complete(task.getId()); + waitForHistoryJobExecutorToProcessAllJobs(10000, 400); + + historyService.deleteHistoricProcessInstance(processInstance.getId()); + + assertThat(findHistoricVariableInstanceById(dependentVariable.getId())).isNull(); + assertThat(findByteArrayById(byteArrayId)).isNull(); + } finally { + processEngineConfiguration.getDependentScopeTypes().remove(TEST_DEPENDENT_SCOPE_TYPE); + } + } + + @Test + @Deployment(resources = { "org/flowable/engine/test/bpmn/oneTask.bpmn20.xml" }) + public void bulkDeleteHistoricProcessInstancesRemovesDependentScopeVariables() { + processEngineConfiguration.addDependentScopeType(TEST_DEPENDENT_SCOPE_TYPE); + try { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("startToEnd"); + HistoricVariableInstanceEntity dependentVariable = createDependentScopeHistoricVariable(processInstance.getId()); + String byteArrayId = dependentVariable.getByteArrayRef().getId(); + assertThat(byteArrayId).isNotNull(); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.complete(task.getId()); + waitForHistoryJobExecutorToProcessAllJobs(10000, 400); + + historyService.bulkDeleteHistoricProcessInstances(Collections.singletonList(processInstance.getId())); + + assertThat(findHistoricVariableInstanceById(dependentVariable.getId())).isNull(); + assertThat(findByteArrayById(byteArrayId)).isNull(); + } finally { + processEngineConfiguration.getDependentScopeTypes().remove(TEST_DEPENDENT_SCOPE_TYPE); + } + } + + /** + * Mimics a consumer that records history for its own dependent scope type: the row has no process instance id, + * only the process instance id as scope id and an execution id as sub scope id. + */ + protected HistoricVariableInstanceEntity createDependentScopeHistoricVariable(String processInstanceId) { + String executionId = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).onlyChildExecutions().singleResult().getId(); + return managementService.executeCommand(commandContext -> { + VariableServiceConfiguration variableServiceConfiguration = processEngineConfiguration.getVariableServiceConfiguration(); + HistoricVariableService historicVariableService = variableServiceConfiguration.getHistoricVariableService(); + + HistoricVariableInstanceEntity historicVariable = historicVariableService.createHistoricVariableInstance(); + historicVariable.setName("dependentScopeVariable"); + historicVariable.setScopeId(processInstanceId); + historicVariable.setSubScopeId(executionId); + historicVariable.setScopeType(TEST_DEPENDENT_SCOPE_TYPE); + historicVariable.setVariableType(variableServiceConfiguration.getVariableTypes().getVariableType(SerializableType.TYPE_NAME)); + historicVariable.setCreateTime(new Date()); + historicVariable.setLastUpdatedTime(historicVariable.getCreateTime()); + historicVariable.setBytes("dependent scope value".getBytes(StandardCharsets.UTF_8)); + + historicVariableService.insertHistoricVariableInstance(historicVariable); + return historicVariable; + }); + } + + protected HistoricVariableInstanceEntity findHistoricVariableInstanceById(String id) { + return managementService.executeCommand(commandContext -> processEngineConfiguration.getVariableServiceConfiguration() + .getHistoricVariableService().getHistoricVariableInstance(id)); + } + + protected ByteArrayEntity findByteArrayById(String id) { + return managementService.executeCommand( + commandContext -> CommandContextUtil.getByteArrayEntityManager(commandContext).findById(id)); + } + protected void validateEmptyHistoricDataForProcessInstance(String processInstanceId) { assertThat(historyService.createHistoricDetailQuery().processInstanceId(processInstanceId).list()).hasSize(0); assertThat(historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list()).hasSize(0); diff --git a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/HistoricVariableService.java b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/HistoricVariableService.java index e11a521ec1d..94c36add02b 100644 --- a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/HistoricVariableService.java +++ b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/HistoricVariableService.java @@ -48,10 +48,14 @@ public interface HistoricVariableService { void deleteHistoricVariableInstancesByProcessInstanceId(String processInstanceId); void deleteHistoricVariableInstancesByTaskId(String taskId); + + void deleteHistoricVariableInstancesByScopeIdAndScopeTypes(String scopeId, Collection scopeTypes); void bulkDeleteHistoricVariableInstancesByProcessInstanceIds(Collection processInstanceIds); void bulkDeleteHistoricVariableInstancesByTaskIds(Collection taskIds); + + void bulkDeleteHistoricVariableInstancesByScopeIdsAndScopeTypes(Collection scopeIds, Collection scopeTypes); void deleteHistoricVariableInstancesForNonExistingProcessInstances(); diff --git a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/HistoricVariableServiceImpl.java b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/HistoricVariableServiceImpl.java index a523fdb3fad..1db47962440 100644 --- a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/HistoricVariableServiceImpl.java +++ b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/HistoricVariableServiceImpl.java @@ -108,6 +108,11 @@ public void deleteHistoricVariableInstancesByTaskId(String taskId) { getHistoricVariableInstanceEntityManager().deleteHistoricVariableInstancesByTaskId(taskId); } + @Override + public void deleteHistoricVariableInstancesByScopeIdAndScopeTypes(String scopeId, Collection scopeTypes) { + getHistoricVariableInstanceEntityManager().deleteHistoricVariableInstancesByScopeIdAndScopeTypes(scopeId, scopeTypes); + } + @Override public void bulkDeleteHistoricVariableInstancesByProcessInstanceIds(Collection processInstanceIds) { getHistoricVariableInstanceEntityManager().bulkDeleteHistoricVariableInstancesByProcessInstanceIds(processInstanceIds); @@ -118,6 +123,11 @@ public void bulkDeleteHistoricVariableInstancesByTaskIds(Collection task getHistoricVariableInstanceEntityManager().bulkDeleteHistoricVariableInstancesByTaskIds(taskIds); } + @Override + public void bulkDeleteHistoricVariableInstancesByScopeIdsAndScopeTypes(Collection scopeIds, Collection scopeTypes) { + getHistoricVariableInstanceEntityManager().bulkDeleteHistoricVariableInstancesByScopeIdsAndScopeTypes(scopeIds, scopeTypes); + } + @Override public void deleteHistoricVariableInstancesForNonExistingProcessInstances() { getHistoricVariableInstanceEntityManager().deleteHistoricVariableInstancesForNonExistingProcessInstances(); diff --git a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/HistoricVariableInstanceEntityManager.java b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/HistoricVariableInstanceEntityManager.java index 6b1f23886f6..0e8a51a64f5 100644 --- a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/HistoricVariableInstanceEntityManager.java +++ b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/HistoricVariableInstanceEntityManager.java @@ -48,6 +48,8 @@ public interface HistoricVariableInstanceEntityManager extends EntityManager findHistoricalVariableInstancesByScopeIdAndScopeType(String scopeId, String scopeType, Collection variableNames); + List findHistoricalVariableInstancesByScopeIdAndScopeTypes(String scopeId, Collection scopeTypes); + List findHistoricalVariableInstancesBySubScopeIdAndScopeType(String subScopeId, String scopeType); long findHistoricVariableInstanceCountByQueryCriteria(HistoricVariableInstanceQueryImpl historicProcessVariableQuery); @@ -59,6 +61,8 @@ public interface HistoricVariableInstanceEntityManager extends EntityManager scopeTypes); void bulkDeleteHistoricVariableInstancesByProcessInstanceIds(Collection processInstanceIds); @@ -66,6 +70,8 @@ public interface HistoricVariableInstanceEntityManager extends EntityManager scopeIds, String scopeType); + void bulkDeleteHistoricVariableInstancesByScopeIdsAndScopeTypes(Collection scopeIds, Collection scopeTypes); + void deleteHistoricVariableInstancesForNonExistingProcessInstances(); void deleteHistoricVariableInstancesForNonExistingCaseInstances(); diff --git a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/HistoricVariableInstanceEntityManagerImpl.java b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/HistoricVariableInstanceEntityManagerImpl.java index 4e2db08613c..26685ed61bc 100644 --- a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/HistoricVariableInstanceEntityManagerImpl.java +++ b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/HistoricVariableInstanceEntityManagerImpl.java @@ -106,6 +106,14 @@ public void deleteHistoricVariableInstanceByProcessInstanceId(final String histo } } + @Override + public void deleteHistoricVariableInstancesByScopeIdAndScopeTypes(String scopeId, Collection scopeTypes) { + List historicVariables = dataManager.findHistoricalVariableInstancesByScopeIdAndScopeTypes(scopeId, scopeTypes); + for (HistoricVariableInstanceEntity historicVariable : historicVariables) { + delete(historicVariable); + } + } + @Override public long findHistoricVariableInstanceCountByQueryCriteria(HistoricVariableInstanceQueryImpl historicProcessVariableQuery) { return dataManager.findHistoricVariableInstanceCountByQueryCriteria(historicProcessVariableQuery); @@ -147,6 +155,11 @@ public List findHistoricalVariableInstancesBySco return dataManager.findHistoricalVariableInstancesByScopeIdAndScopeType(scopeId, scopeType, variableNames); } + @Override + public List findHistoricalVariableInstancesByScopeIdAndScopeTypes(String scopeId, Collection scopeTypes) { + return dataManager.findHistoricalVariableInstancesByScopeIdAndScopeTypes(scopeId, scopeTypes); + } + @Override public List findHistoricalVariableInstancesBySubScopeIdAndScopeType(String subScopeId, String scopeType) { return dataManager.findHistoricalVariableInstancesBySubScopeIdAndScopeType(subScopeId, scopeType); @@ -175,6 +188,11 @@ public void bulkDeleteHistoricVariableInstancesByScopeIdsAndScopeType(Collection dataManager.bulkDeleteHistoricVariableInstancesByScopeIdsAndScopeType(scopeIds, scopeType); } + @Override + public void bulkDeleteHistoricVariableInstancesByScopeIdsAndScopeTypes(Collection scopeIds, Collection scopeTypes) { + dataManager.bulkDeleteHistoricVariableInstancesByScopeIdsAndScopeTypes(scopeIds, scopeTypes); + } + @Override public void deleteHistoricVariableInstancesForNonExistingProcessInstances() { dataManager.deleteHistoricVariableInstancesForNonExistingProcessInstances(); diff --git a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/data/HistoricVariableInstanceDataManager.java b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/data/HistoricVariableInstanceDataManager.java index 76af032399f..f37b81a20e9 100644 --- a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/data/HistoricVariableInstanceDataManager.java +++ b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/data/HistoricVariableInstanceDataManager.java @@ -42,6 +42,8 @@ public interface HistoricVariableInstanceDataManager extends DataManager findHistoricalVariableInstancesByScopeIdAndScopeType(String scopeId, String scopeType, Collection variableNames); + List findHistoricalVariableInstancesByScopeIdAndScopeTypes(String scopeId, Collection scopeTypes); + List findHistoricalVariableInstancesBySubScopeIdAndScopeType(String subScopeId, String scopeType); List findHistoricVariableInstancesByNativeQuery(Map parameterMap); @@ -54,6 +56,8 @@ public interface HistoricVariableInstanceDataManager extends DataManager scopeIds, String scopeType); + void bulkDeleteHistoricVariableInstancesByScopeIdsAndScopeTypes(Collection scopeIds, Collection scopeTypes); + void deleteHistoricVariableInstancesForNonExistingProcessInstances(); void deleteHistoricVariableInstancesForNonExistingCaseInstances(); diff --git a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/data/impl/MybatisHistoricVariableInstanceDataManager.java b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/data/impl/MybatisHistoricVariableInstanceDataManager.java index 05bd1a71dc4..fa63340a57c 100644 --- a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/data/impl/MybatisHistoricVariableInstanceDataManager.java +++ b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/data/impl/MybatisHistoricVariableInstanceDataManager.java @@ -13,6 +13,7 @@ package org.flowable.variable.service.impl.persistence.entity.data.impl; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -28,6 +29,7 @@ import org.flowable.variable.service.impl.persistence.entity.data.HistoricVariableInstanceDataManager; import org.flowable.variable.service.impl.persistence.entity.data.impl.cachematcher.HistoricVariableInstanceByProcInstMatcher; import org.flowable.variable.service.impl.persistence.entity.data.impl.cachematcher.HistoricVariableInstanceByScopeIdAndScopeTypeMatcher; +import org.flowable.variable.service.impl.persistence.entity.data.impl.cachematcher.HistoricVariableInstanceByScopeIdAndScopeTypesMatcher; import org.flowable.variable.service.impl.persistence.entity.data.impl.cachematcher.HistoricVariableInstanceBySubScopeIdAndScopeTypeMatcher; import org.flowable.variable.service.impl.persistence.entity.data.impl.cachematcher.HistoricVariableInstanceByTaskIdMatcher; @@ -45,6 +47,9 @@ public class MybatisHistoricVariableInstanceDataManager extends AbstractDataMana protected CachedEntityMatcher historicVariableInstanceByScopeIdAndScopeTypeMatcher = new HistoricVariableInstanceByScopeIdAndScopeTypeMatcher(); + protected CachedEntityMatcher historicVariableInstanceByScopeIdAndScopeTypesMatcher + = new HistoricVariableInstanceByScopeIdAndScopeTypesMatcher(); + protected CachedEntityMatcher historicVariableInstanceBySubScopeIdAndScopeTypeMatcher = new HistoricVariableInstanceBySubScopeIdAndScopeTypeMatcher(); @@ -118,12 +123,24 @@ public List findHistoricalVariableInstancesBySco return getList("selectHistoricVariableInstanceByScopeIdAndScopeType", params, historicVariableInstanceByScopeIdAndScopeTypeMatcher, true); } + @Override + public List findHistoricalVariableInstancesByScopeIdAndScopeTypes(String scopeId, Collection scopeTypes) { + if (scopeTypes == null || scopeTypes.isEmpty()) { + return Collections.emptyList(); + } + + Map params = new HashMap<>(2); + params.put("scopeId", scopeId); + params.put("scopeTypes", scopeTypes); + return getList("selectHistoricVariableInstanceByScopeIdAndScopeTypes", params, historicVariableInstanceByScopeIdAndScopeTypesMatcher, true); + } + @Override public List findHistoricalVariableInstancesBySubScopeIdAndScopeType(String subScopeId, String scopeType) { Map params = new HashMap<>(2); params.put("subScopeId", subScopeId); params.put("scopeType", scopeType); - return getList("selectHistoricVariableInstanceByScopeIdAndScopeType", params, historicVariableInstanceByScopeIdAndScopeTypeMatcher, true); + return getList("selectHistoricVariableInstanceBySubScopeIdAndScopeType", params, historicVariableInstanceBySubScopeIdAndScopeTypeMatcher, true); } @Override @@ -162,6 +179,21 @@ public void bulkDeleteHistoricVariableInstancesByScopeIdsAndScopeType(Collection getDbSqlSession().delete("bulkDeleteHistoricVariableInstancesForScopeIdsAndScopeType", params, HistoricVariableInstanceEntity.class); } + @Override + public void bulkDeleteHistoricVariableInstancesByScopeIdsAndScopeTypes(Collection scopeIds, Collection scopeTypes) { + if (scopeTypes == null || scopeTypes.isEmpty()) { + return; + } + + Map params = new HashMap<>(2); + params.put("scopeIds", createSafeInValuesList(scopeIds)); + params.put("scopeTypes", scopeTypes); + + // Using HistoricVariableInstanceEntity as the entity, because the deletion order of the ByteArrayEntity is after the HistoricVariableInstanceEntity + getDbSqlSession().delete("bulkDeleteBytesForHistoricVariableInstancesForScopeIdsAndScopeTypes", params, HistoricVariableInstanceEntity.class); + getDbSqlSession().delete("bulkDeleteHistoricVariableInstancesForScopeIdsAndScopeTypes", params, HistoricVariableInstanceEntity.class); + } + @Override public void deleteHistoricVariableInstancesForNonExistingProcessInstances() { // Using HistoricVariableInstanceEntity as the entity, because the deletion order of the ByteArrayEntity is after the HistoricVariableInstanceEntity diff --git a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/data/impl/cachematcher/HistoricVariableInstanceByScopeIdAndScopeTypesMatcher.java b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/data/impl/cachematcher/HistoricVariableInstanceByScopeIdAndScopeTypesMatcher.java new file mode 100644 index 00000000000..d9b97311fb1 --- /dev/null +++ b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/persistence/entity/data/impl/cachematcher/HistoricVariableInstanceByScopeIdAndScopeTypesMatcher.java @@ -0,0 +1,35 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.flowable.variable.service.impl.persistence.entity.data.impl.cachematcher; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; + +import org.flowable.common.engine.impl.persistence.cache.CachedEntityMatcherAdapter; +import org.flowable.variable.service.impl.persistence.entity.HistoricVariableInstanceEntity; + +/** + * @author Filip Hrisafov + */ +public class HistoricVariableInstanceByScopeIdAndScopeTypesMatcher extends CachedEntityMatcherAdapter { + + @Override + @SuppressWarnings("unchecked") + public boolean isRetained(HistoricVariableInstanceEntity historicVariableInstanceEntity, Object parameter) { + Map map = (Map) parameter; + return Objects.equals(historicVariableInstanceEntity.getScopeId(), map.get("scopeId")) + && ((Collection) map.get("scopeTypes")).contains(historicVariableInstanceEntity.getScopeType()); + } + +} diff --git a/modules/flowable-variable-service/src/main/resources/org/flowable/variable/service/db/mapping/entity/HistoricVariableInstance.xml b/modules/flowable-variable-service/src/main/resources/org/flowable/variable/service/db/mapping/entity/HistoricVariableInstance.xml index b00805f9c42..6b80b43e7ad 100644 --- a/modules/flowable-variable-service/src/main/resources/org/flowable/variable/service/db/mapping/entity/HistoricVariableInstance.xml +++ b/modules/flowable-variable-service/src/main/resources/org/flowable/variable/service/db/mapping/entity/HistoricVariableInstance.xml @@ -247,6 +247,45 @@ ) and SCOPE_TYPE_ = #{scopeType, jdbcType=NVARCHAR} + + + delete BYTES_ from ${prefix}ACT_GE_BYTEARRAY BYTES_ + where BYTES_.ID_ in ( + select VARINST.BYTEARRAY_ID_ + from ${prefix}ACT_HI_VARINST VARINST + where + ( + + or + + VARINST.SCOPE_ID_ in + + #{scopeId, jdbcType=NVARCHAR} + + ) + and VARINST.SCOPE_TYPE_ in + + #{scopeType, jdbcType=NVARCHAR} + + ) + + + + delete from ${prefix}ACT_HI_VARINST where + ( + + or + + SCOPE_ID_ in + + #{scopeId, jdbcType=NVARCHAR} + + ) + and SCOPE_TYPE_ in + + #{scopeType, jdbcType=NVARCHAR} + + delete VARINST from ${prefix}ACT_HI_VARINST VARINST where VARINST.PROC_INST_ID_ is not null and VARINST.PROC_INST_ID_ != '' and @@ -550,6 +589,16 @@ + +