Skip to content

Commit 3cb04ed

Browse files
committed
Keep the parent deployment id on the cached process and case definitions
The deployers copy the parent deployment id of the deployment onto the cached process and case definitions, so resolving the definition deployment id no longer needs to select the deployment. The start form, form definitions and decisions lookups of BPMN and CMMN (and the REST start form resources) now use the parent deployment id of the cached definitions instead of their own deployment lookups. The selects are verified with database operation count tests for a case with a case task, a case with a process task, a process with a case task, a process and a case with a decision task, and getting the decisions of a process and a case definition.
1 parent c5a3dfa commit 3cb04ed

20 files changed

Lines changed: 566 additions & 57 deletions

File tree

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/* Licensed under the Apache License, Version 2.0 (the "License");
2+
* you may not use this file except in compliance with the License.
3+
* You may obtain a copy of the License at
4+
*
5+
* http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
package org.flowable.cmmn.test;
14+
15+
import static org.assertj.core.api.Assertions.assertThat;
16+
import static org.assertj.core.api.Assertions.entry;
17+
18+
import java.util.Map;
19+
20+
import org.flowable.common.engine.impl.cfg.CommandExecutorImpl;
21+
import org.flowable.common.engine.impl.db.DbSqlSessionFactory;
22+
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
23+
import org.flowable.common.engine.impl.interceptor.CommandInterceptor;
24+
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
25+
import org.flowable.engine.test.profiler.FlowableProfiler;
26+
import org.flowable.engine.test.profiler.ProfilingDbSqlSessionFactory;
27+
import org.flowable.engine.test.profiler.TotalExecutionTimeCommandInterceptor;
28+
import org.junit.jupiter.api.AfterEach;
29+
import org.junit.jupiter.api.BeforeEach;
30+
import org.junit.jupiter.api.Test;
31+
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
32+
33+
/**
34+
* @author Filip Hrisafov
35+
*/
36+
@DisabledIfSystemProperty(named = "disableWhen", matches = "cockroachdb")
37+
public class VerifyDatabaseOperationsTest extends AbstractProcessEngineIntegrationTest {
38+
39+
protected DbSqlSessionFactory oldDbSqlSessionFactory;
40+
41+
@BeforeEach
42+
protected void setUp() {
43+
// The process and the CMMN engine share the session factories
44+
ProcessEngineConfigurationImpl processEngineConfigurationImpl = (ProcessEngineConfigurationImpl) processEngineConfiguration;
45+
this.oldDbSqlSessionFactory = processEngineConfigurationImpl.getDbSqlSessionFactory();
46+
DbSqlSessionFactory newDbSqlSessionFactory = new ProfilingDbSqlSessionFactory(processEngineConfigurationImpl.isUsePrefixId());
47+
newDbSqlSessionFactory.setBulkInserteableEntityClasses(oldDbSqlSessionFactory.getBulkInserteableEntityClasses());
48+
newDbSqlSessionFactory.setInsertionOrder(oldDbSqlSessionFactory.getInsertionOrder());
49+
newDbSqlSessionFactory.setDeletionOrder(oldDbSqlSessionFactory.getDeletionOrder());
50+
newDbSqlSessionFactory.setDatabaseType(oldDbSqlSessionFactory.getDatabaseType());
51+
newDbSqlSessionFactory.setDatabaseTablePrefix(oldDbSqlSessionFactory.getDatabaseTablePrefix());
52+
newDbSqlSessionFactory.setTablePrefixIsSchema(oldDbSqlSessionFactory.isTablePrefixIsSchema());
53+
newDbSqlSessionFactory.setDatabaseCatalog(oldDbSqlSessionFactory.getDatabaseCatalog());
54+
newDbSqlSessionFactory.setDatabaseSchema(oldDbSqlSessionFactory.getDatabaseSchema());
55+
newDbSqlSessionFactory.setSqlSessionFactory(oldDbSqlSessionFactory.getSqlSessionFactory());
56+
newDbSqlSessionFactory.setDbHistoryUsed(oldDbSqlSessionFactory.isDbHistoryUsed());
57+
newDbSqlSessionFactory.setDatabaseSpecificStatements(oldDbSqlSessionFactory.getDatabaseSpecificStatements());
58+
newDbSqlSessionFactory.setLogicalNameToClassMapping(oldDbSqlSessionFactory.getLogicalNameToClassMapping());
59+
processEngineConfigurationImpl.addSessionFactory(newDbSqlSessionFactory);
60+
}
61+
62+
@AfterEach
63+
protected void tearDown() {
64+
((ProcessEngineConfigurationImpl) processEngineConfiguration).addSessionFactory(oldDbSqlSessionFactory);
65+
FlowableProfiler.getInstance().reset();
66+
}
67+
68+
@Test
69+
public void testCaseWithProcessTaskFromSameDeployment() {
70+
processEngineRepositoryService.createDeployment()
71+
.addClasspathResource("org/flowable/cmmn/test/processTaskSameDeploymentTrue.cmmn")
72+
.addClasspathResource("org/flowable/cmmn/test/oneTaskProcess.bpmn20.xml")
73+
.deploy();
74+
75+
Map<String, Long> selects = profileDatabaseSelects(cmmnEngineConfiguration.getCommandExecutor(), "org.flowable.cmmn.engine.impl.cmd.StartCaseInstanceCmd",
76+
() -> cmmnRuntimeService.createCaseInstanceBuilder()
77+
.caseDefinitionKey("oneProcessTaskCase")
78+
.start());
79+
80+
// The cached case definition has the parent deployment id, so the CMMN deployment is not selected
81+
assertThat(selects).containsOnly(
82+
entry("selectLatestCaseDefinitionByKey", 1L),
83+
entry("selectProcessDefinitionByParentDeploymentAndKey", 1L),
84+
entry("selectProcessDefinitionInfoByProcessDefinitionId", 1L),
85+
entry("selectPlanItemInstancesByCaseInstanceIdAndPlanItemId", 1L),
86+
entry("selectEntityLinksByQuery", 2L)
87+
);
88+
assertThat(processEngineRuntimeService.createProcessInstanceQuery().count()).isOne();
89+
}
90+
91+
@Test
92+
public void testCaseWithCaseTaskFromSameDeployment() {
93+
cmmnRepositoryService.createDeployment()
94+
.addClasspathResource("org/flowable/cmmn/test/caseTaskSameDeploymentTrue.cmmn")
95+
.addClasspathResource("org/flowable/cmmn/test/oneHumanTaskCase.cmmn")
96+
.deploy();
97+
98+
Map<String, Long> selects = profileDatabaseSelects(cmmnEngineConfiguration.getCommandExecutor(), "org.flowable.cmmn.engine.impl.cmd.StartCaseInstanceCmd",
99+
() -> cmmnRuntimeService.createCaseInstanceBuilder()
100+
.caseDefinitionKey("oneCaseTaskCase")
101+
.start());
102+
103+
// The cached case definition has the parent deployment id, so the CMMN deployment is not selected
104+
assertThat(selects).containsOnly(
105+
entry("selectLatestCaseDefinitionByKey", 1L),
106+
entry("selectCaseDefinitionByParentDeploymentAndKey", 1L),
107+
entry("selectPlanItemInstancesByCaseInstanceIdAndPlanItemId", 2L),
108+
entry("selectEntityLinksByQuery", 2L)
109+
);
110+
assertThat(cmmnRuntimeService.createCaseInstanceQuery().caseDefinitionKey("oneHumanTaskCase").count()).isOne();
111+
}
112+
113+
@Test
114+
public void testProcessWithCaseTaskFromSameDeployment() {
115+
processEngineRepositoryService.createDeployment()
116+
.addClasspathResource("org/flowable/cmmn/test/caseTaskProcessWithSameDeployment.bpmn20.xml")
117+
.addClasspathResource("org/flowable/cmmn/test/oneHumanTaskCase.cmmn")
118+
.deploy();
119+
120+
Map<String, Long> selects = profileDatabaseSelects(processEngineConfiguration.getCommandExecutor(), "org.flowable.engine.impl.cmd.StartProcessInstanceCmd",
121+
() -> processEngineRuntimeService.createProcessInstanceBuilder()
122+
.processDefinitionKey("caseTask")
123+
.start());
124+
125+
// The cached process definition has the parent deployment id, so the process deployment is not selected
126+
assertThat(selects).containsOnly(
127+
entry("selectLatestProcessDefinitionByKey", 1L),
128+
entry("selectCaseDefinitionByParentDeploymentAndKey", 1L),
129+
entry("selectProcessDefinitionInfoByProcessDefinitionId", 1L),
130+
entry("selectPlanItemInstancesByCaseInstanceIdAndPlanItemId", 1L),
131+
entry("selectEntityLinksByQuery", 2L)
132+
);
133+
assertThat(cmmnRuntimeService.createCaseInstanceQuery().count()).isOne();
134+
}
135+
136+
protected Map<String, Long> profileDatabaseSelects(CommandExecutor commandExecutor, String commandClass, Runnable runnable) {
137+
// Only the outer command is profiled, the commands of the other engine reuse its command context
138+
CommandExecutorImpl commandExecutorImpl = (CommandExecutorImpl) commandExecutor;
139+
CommandInterceptor oldFirstCommandInterceptor = commandExecutorImpl.getFirst();
140+
TotalExecutionTimeCommandInterceptor timeCommandInterceptor = new TotalExecutionTimeCommandInterceptor();
141+
timeCommandInterceptor.setNext(oldFirstCommandInterceptor);
142+
commandExecutorImpl.setFirst(timeCommandInterceptor);
143+
144+
FlowableProfiler profiler = FlowableProfiler.getInstance();
145+
profiler.reset();
146+
profiler.startProfileSession("Profiling session");
147+
try {
148+
runnable.run();
149+
} finally {
150+
profiler.stopCurrentProfileSession();
151+
commandExecutorImpl.setFirst(oldFirstCommandInterceptor);
152+
}
153+
154+
return profiler.getProfileSessions().get(0).calculateSummaryStatistics().get(commandClass).getDbSelects();
155+
}
156+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<definitions xmlns="http://www.omg.org/spec/CMMN/20151109/MODEL"
3+
xmlns:flowable="http://flowable.org/cmmn"
4+
targetNamespace="http://flowable.org/cmmn">
5+
6+
<case id="oneCaseTaskCase">
7+
<casePlanModel id="myPlanModel" name="My CasePlanModel">
8+
9+
<planItem id="planItem1" name="The Case" definitionRef="theCase"/>
10+
11+
<caseTask id="theCase" caseRef="oneHumanTaskCase" flowable:sameDeployment="true"/>
12+
13+
</casePlanModel>
14+
</case>
15+
16+
</definitions>

‎modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/GetDecisionsForCaseDefinitionCmd.java‎

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.util.Set;
2020

2121
import org.flowable.cmmn.api.repository.CaseDefinition;
22-
import org.flowable.cmmn.api.repository.CmmnDeployment;
2322
import org.flowable.cmmn.engine.impl.repository.CaseDefinitionUtil;
2423
import org.flowable.cmmn.engine.impl.util.CommandContextUtil;
2524
import org.flowable.cmmn.model.Case;
@@ -96,16 +95,11 @@ protected List<DmnDecision> getDecisionsFromModel(Case caseModel, CaseDefinition
9695

9796
protected void addDecisionToCollection(List<DmnDecision> decisions, String decisionKey, CaseDefinition caseDefinition) {
9897
DmnDecisionQuery definitionQuery = dmnRepositoryService.createDecisionQuery().decisionKey(decisionKey);
99-
CmmnDeployment deployment = CommandContextUtil.getCmmnDeploymentEntityManager().findById(caseDefinition.getDeploymentId());
100-
if (deployment.getParentDeploymentId() != null) {
101-
List<DmnDeployment> dmnDeployments = dmnRepositoryService.createDeploymentQuery().parentDeploymentId(deployment.getParentDeploymentId()).list();
102-
103-
if (dmnDeployments != null && dmnDeployments.size() > 0) {
104-
definitionQuery.deploymentId(dmnDeployments.get(0).getId());
105-
} else {
106-
definitionQuery.latestVersion();
107-
}
108-
98+
String parentDeploymentId = CaseDefinitionUtil.getDefinitionDeploymentId(caseDefinition, CommandContextUtil.getCmmnEngineConfiguration());
99+
List<DmnDeployment> dmnDeployments = dmnRepositoryService.createDeploymentQuery().parentDeploymentId(parentDeploymentId).list();
100+
101+
if (!dmnDeployments.isEmpty()) {
102+
definitionQuery.deploymentId(dmnDeployments.get(0).getId());
109103
} else {
110104
definitionQuery.latestVersion();
111105
}

‎modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/GetFormDefinitionsForCaseDefinitionCmd.java‎

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
import org.apache.commons.lang3.StringUtils;
2222
import org.flowable.cmmn.api.repository.CaseDefinition;
23-
import org.flowable.cmmn.api.repository.CmmnDeployment;
2423
import org.flowable.cmmn.engine.impl.repository.CaseDefinitionUtil;
2524
import org.flowable.cmmn.engine.impl.util.CommandContextUtil;
2625
import org.flowable.cmmn.model.Case;
@@ -94,16 +93,11 @@ protected List<FormDefinition> getFormDefinitionsFromModel(Case caseModel, CaseD
9493

9594
protected void addFormDefinitionToCollection(List<FormDefinition> formDefinitions, String formKey, CaseDefinition caseDefinition) {
9695
FormDefinitionQuery formDefinitionQuery = formRepositoryService.createFormDefinitionQuery().formDefinitionKey(formKey);
97-
CmmnDeployment deployment = CommandContextUtil.getCmmnDeploymentEntityManager().findById(caseDefinition.getDeploymentId());
98-
if (deployment.getParentDeploymentId() != null) {
99-
List<FormDeployment> formDeployments = formRepositoryService.createDeploymentQuery().parentDeploymentId(deployment.getParentDeploymentId()).list();
100-
101-
if (formDeployments != null && formDeployments.size() > 0) {
102-
formDefinitionQuery.deploymentId(formDeployments.get(0).getId());
103-
} else {
104-
formDefinitionQuery.latestVersion();
105-
}
106-
96+
String parentDeploymentId = CaseDefinitionUtil.getDefinitionDeploymentId(caseDefinition, CommandContextUtil.getCmmnEngineConfiguration());
97+
List<FormDeployment> formDeployments = formRepositoryService.createDeploymentQuery().parentDeploymentId(parentDeploymentId).list();
98+
99+
if (!formDeployments.isEmpty()) {
100+
formDefinitionQuery.deploymentId(formDeployments.get(0).getId());
107101
} else {
108102
formDefinitionQuery.latestVersion();
109103
}

‎modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/GetStartFormModelCmd.java‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import org.apache.commons.lang3.StringUtils;
1818
import org.flowable.cmmn.api.repository.CaseDefinition;
19-
import org.flowable.cmmn.api.repository.CmmnDeployment;
2019
import org.flowable.cmmn.engine.CmmnEngineConfiguration;
2120
import org.flowable.cmmn.engine.impl.repository.CaseDefinitionUtil;
2221
import org.flowable.cmmn.engine.impl.util.CommandContextUtil;
@@ -62,8 +61,8 @@ public FormInfo execute(CommandContext commandContext) {
6261
Stage planModel = caseModel.getPlanModel();
6362

6463
if (StringUtils.isNotEmpty(planModel.getFormKey())) {
65-
CmmnDeployment deployment = CommandContextUtil.getCmmnDeploymentEntityManager(commandContext).findById(caseDefinition.getDeploymentId());
66-
formInfo = formService.getFormInstanceModelByKeyAndParentDeploymentIdAndScopeId(planModel.getFormKey(), deployment.getParentDeploymentId(),
64+
String parentDeploymentId = CaseDefinitionUtil.getDefinitionDeploymentId(caseDefinition, cmmnEngineConfiguration);
65+
formInfo = formService.getFormInstanceModelByKeyAndParentDeploymentIdAndScopeId(planModel.getFormKey(), parentDeploymentId,
6766
caseInstanceId, ScopeTypes.CMMN, null, caseDefinition.getTenantId(), cmmnEngineConfiguration.isFallbackToDefaultTenant());
6867
}
6968

‎modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/deployer/CmmnDeployer.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,12 +275,14 @@ protected void verifyCaseDefinitionsDoNotShareKeys(Collection<CaseDefinitionEnti
275275
protected void copyDeploymentValuesToCaseDefinitions(EngineDeployment deployment, List<CaseDefinitionEntity> caseDefinitionEntities) {
276276
String tenantId = deployment.getTenantId();
277277
String deploymentId = deployment.getId();
278+
String parentDeploymentId = ((CmmnDeploymentEntity) deployment).getParentDeploymentId();
278279

279280
for (CaseDefinitionEntity caseDefinitionEntity : caseDefinitionEntities) {
280281
if (tenantId != null) {
281282
caseDefinitionEntity.setTenantId(tenantId);
282283
}
283284
caseDefinitionEntity.setDeploymentId(deploymentId);
285+
caseDefinitionEntity.setParentDeploymentId(parentDeploymentId);
284286
}
285287
}
286288

‎modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/CaseDefinitionEntity.java‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,13 @@ public interface CaseDefinitionEntity extends Entity, CaseDefinition {
4343

4444
void setTenantId(String tenantId);
4545

46+
/**
47+
* The parent deployment id of the deployment of this case definition.
48+
* This is not persisted, it is only set on the case definitions that are put in the case definition cache.
49+
*/
50+
String getParentDeploymentId();
51+
52+
void setParentDeploymentId(String parentDeploymentId);
53+
4654
List<IdentityLinkEntity> getIdentityLinks();
4755
}

‎modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/CaseDefinitionEntityImpl.java‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ public class CaseDefinitionEntityImpl extends AbstractCmmnEngineEntity implement
4141
protected List<IdentityLinkEntity> definitionIdentityLinkEntities = new ArrayList<>();
4242
protected String localizedName;
4343
protected String localizedDescription;
44+
45+
// Not persisted, set by the deployer
46+
protected String parentDeploymentId;
4447

4548
@Override
4649
public Object getPersistentState() {
@@ -198,4 +201,14 @@ public String getLocalizedDescription() {
198201
public void setLocalizedDescription(String localizedDescription) {
199202
this.localizedDescription = localizedDescription;
200203
}
204+
205+
@Override
206+
public String getParentDeploymentId() {
207+
return parentDeploymentId;
208+
}
209+
210+
@Override
211+
public void setParentDeploymentId(String parentDeploymentId) {
212+
this.parentDeploymentId = parentDeploymentId;
213+
}
201214
}

‎modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/repository/CaseDefinitionUtil.java‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.flowable.cmmn.api.repository.CaseDefinition;
1717
import org.flowable.cmmn.engine.CmmnEngineConfiguration;
1818
import org.flowable.cmmn.engine.impl.deployer.CmmnDeploymentManager;
19+
import org.flowable.cmmn.engine.impl.persistence.entity.CaseDefinitionEntity;
1920
import org.flowable.cmmn.engine.impl.persistence.entity.CmmnDeploymentEntity;
2021
import org.flowable.cmmn.engine.impl.persistence.entity.deploy.CaseDefinitionCacheEntry;
2122
import org.flowable.cmmn.engine.impl.util.CommandContextUtil;
@@ -46,12 +47,33 @@ public static String getDefinitionDeploymentId(String caseDefinitionId, CmmnEngi
4647

4748
public static String getDefinitionDeploymentId(CaseDefinition caseDefinition, CmmnEngineConfiguration cmmnEngineConfiguration) {
4849
CmmnDeploymentManager deploymentManager = cmmnEngineConfiguration.getDeploymentManager();
50+
String parentDeploymentId = getParentDeploymentId(caseDefinition);
51+
if (parentDeploymentId == null) {
52+
// A case definition loaded from the database does not have the parent deployment id, the cached one has it
53+
CaseDefinitionCacheEntry cacheEntry = deploymentManager.getCaseDefinitionCache().get(caseDefinition.getId());
54+
if (cacheEntry != null) {
55+
parentDeploymentId = getParentDeploymentId(cacheEntry.getCaseDefinition());
56+
}
57+
}
58+
59+
if (parentDeploymentId != null) {
60+
return parentDeploymentId;
61+
}
62+
4963
CmmnDeploymentEntity caseDeployment = deploymentManager.getDeploymentEntityManager().findById(caseDefinition.getDeploymentId());
5064
if (StringUtils.isEmpty(caseDeployment.getParentDeploymentId())) {
5165
return caseDefinition.getDeploymentId();
5266
}
5367
return caseDeployment.getParentDeploymentId();
5468
}
69+
70+
protected static String getParentDeploymentId(CaseDefinition caseDefinition) {
71+
if (caseDefinition instanceof CaseDefinitionEntity caseDefinitionEntity
72+
&& StringUtils.isNotEmpty(caseDefinitionEntity.getParentDeploymentId())) {
73+
return caseDefinitionEntity.getParentDeploymentId();
74+
}
75+
return null;
76+
}
5577

5678
protected static CaseDefinition getCaseDefinition(String caseDefinitionId, CmmnDeploymentManager deploymentManager, CaseDefinitionCacheEntry cacheEntry) {
5779
if (cacheEntry != null) {

0 commit comments

Comments
 (0)