This is an automated email from the ASF dual-hosted git repository.
mweiler pushed a commit to branch main
in repository
https://gitbox.apache.org/repos/asf/incubator-kie-kogito-runtimes.git
The following commit(s) were added to refs/heads/main by this push:
new 4f6d50a148 [incubator-kie-issues##2168] User Task: Reduce the number
of interactions with the database (#4127)
4f6d50a148 is described below
commit 4f6d50a14804af9561e1427432902cfb968fc23b
Author: Abhiram Gundala <[email protected]>
AuthorDate: Wed Nov 12 09:19:50 2025 -0500
[incubator-kie-issues##2168] User Task: Reduce the number of interactions
with the database (#4127)
* batch update
* Added Tests
---
.../handler/UserTaskKogitoWorkItemHandler.java | 68 ++++++++++---------
jbpm/jbpm-usertask/pom.xml | 5 ++
.../usertask/impl/DefaultUserTaskInstance.java | 33 +++++++--
.../usertask/impl/DefaultUserTaskInstanceTest.java | 78 ++++++++++++++++++++++
4 files changed, 144 insertions(+), 40 deletions(-)
diff --git
a/jbpm/jbpm-usertask-workitem/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandler.java
b/jbpm/jbpm-usertask-workitem/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandler.java
index 80a6ad977e..ffb2a33a34 100644
---
a/jbpm/jbpm-usertask-workitem/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandler.java
+++
b/jbpm/jbpm-usertask-workitem/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandler.java
@@ -83,39 +83,41 @@ public class UserTaskKogitoWorkItemHandler extends
DefaultKogitoWorkItemHandler
userTask.instances().create(instance);
- instance.setTaskName(ofNullable((String)
workItem.getParameter(TASK_NAME)).orElse((String)
workItem.getParameter(NODE_NAME)));
- instance.setTaskDescription((String)
workItem.getParameter(DESCRIPTION));
- instance.setTaskPriority(priority != null ? priority.toString() :
null);
- instance.setSlaDueDate(workItem.getNodeInstance().getSlaDueDate());
-
- Map<String, Object> metadata = new HashMap<>();
- metadata.put("ProcessId",
workItem.getProcessInstance().getProcessId());
- metadata.put("ProcessType",
workItem.getProcessInstance().getProcess().getType());
- metadata.put("ProcessVersion",
workItem.getProcessInstance().getProcessVersion());
- metadata.put("ProcessInstanceId",
workItem.getProcessInstance().getId());
- metadata.put("ProcessInstanceState",
workItem.getProcessInstance().getState());
- metadata.put("RootProcessId",
workItem.getProcessInstance().getRootProcessId());
- metadata.put("RootProcessInstanceId",
workItem.getProcessInstance().getRootProcessInstanceId());
- metadata.put("ParentProcessInstanceId",
workItem.getProcessInstance().getParentProcessInstanceId());
- metadata.put("NodeInstanceId", workItem.getNodeInstance().getId());
-
- instance.setMetadata(metadata);
-
- instance.fireInitialStateChange();
- workItem.getParameters().entrySet().stream().filter(e ->
!HumanTaskNode.TASK_PARAMETERS.contains(e.getKey())).forEach(e ->
instance.setInput(e.getKey(), e.getValue()));
-
-
ofNullable(workItem.getParameters().get(ACTOR_ID)).map(String.class::cast).map(this::toSet).ifPresent(instance::setPotentialUsers);
-
ofNullable(workItem.getParameters().get(GROUP_ID)).map(String.class::cast).map(this::toSet).ifPresent(instance::setPotentialGroups);
-
ofNullable(workItem.getParameters().get(BUSINESSADMINISTRATOR_ID)).map(String.class::cast).map(this::toSet).ifPresent(instance::setAdminUsers);
-
ofNullable(workItem.getParameters().get(BUSINESSADMINISTRATOR_GROUP_ID)).map(String.class::cast).map(this::toSet).ifPresent(instance::setAdminGroups);
-
ofNullable(workItem.getParameters().get(EXCLUDED_OWNER_ID)).map(String.class::cast).map(this::toSet).ifPresent(instance::setExcludedUsers);
-
-
ofNullable(workItem.getParameters().get(NOT_STARTED_NOTIFY)).map(String.class::cast).map(DeadlineHelper::parseDeadlines).ifPresent(instance::setNotStartedDeadlines);
-
ofNullable(workItem.getParameters().get(NOT_STARTED_REASSIGN)).map(String.class::cast).map(DeadlineHelper::parseReassignments).ifPresent(instance::setNotStartedReassignments);
-
ofNullable(workItem.getParameters().get(NOT_COMPLETED_NOTIFY)).map(String.class::cast).map(DeadlineHelper::parseDeadlines).ifPresent(instance::setNotCompletedDeadlines);
-
ofNullable(workItem.getParameters().get(NOT_COMPLETED_REASSIGN)).map(String.class::cast).map(DeadlineHelper::parseReassignments).ifPresent(instance::setNotCompletedReassignments);
-
- instance.initialize(emptyMap(),
IdentityProviders.of(WORKFLOW_ENGINE_USER));
+ instance.batchUpdate(task -> {
+ task.setTaskName(ofNullable((String)
workItem.getParameter(TASK_NAME)).orElse((String)
workItem.getParameter(NODE_NAME)));
+ task.setTaskDescription((String)
workItem.getParameter(DESCRIPTION));
+ task.setTaskPriority(priority != null ? priority.toString() :
null);
+ task.setSlaDueDate(workItem.getNodeInstance().getSlaDueDate());
+
+ Map<String, Object> metadata = new HashMap<>();
+ metadata.put("ProcessId",
workItem.getProcessInstance().getProcessId());
+ metadata.put("ProcessType",
workItem.getProcessInstance().getProcess().getType());
+ metadata.put("ProcessVersion",
workItem.getProcessInstance().getProcessVersion());
+ metadata.put("ProcessInstanceId",
workItem.getProcessInstance().getId());
+ metadata.put("ProcessInstanceState",
workItem.getProcessInstance().getState());
+ metadata.put("RootProcessId",
workItem.getProcessInstance().getRootProcessId());
+ metadata.put("RootProcessInstanceId",
workItem.getProcessInstance().getRootProcessInstanceId());
+ metadata.put("ParentProcessInstanceId",
workItem.getProcessInstance().getParentProcessInstanceId());
+ metadata.put("NodeInstanceId", workItem.getNodeInstance().getId());
+
+ task.setMetadata(metadata);
+
+ task.fireInitialStateChange();
+ workItem.getParameters().entrySet().stream().filter(e ->
!HumanTaskNode.TASK_PARAMETERS.contains(e.getKey())).forEach(e ->
task.setInput(e.getKey(), e.getValue()));
+
+
ofNullable(workItem.getParameters().get(ACTOR_ID)).map(String.class::cast).map(this::toSet).ifPresent(task::setPotentialUsers);
+
ofNullable(workItem.getParameters().get(GROUP_ID)).map(String.class::cast).map(this::toSet).ifPresent(task::setPotentialGroups);
+
ofNullable(workItem.getParameters().get(BUSINESSADMINISTRATOR_ID)).map(String.class::cast).map(this::toSet).ifPresent(task::setAdminUsers);
+
ofNullable(workItem.getParameters().get(BUSINESSADMINISTRATOR_GROUP_ID)).map(String.class::cast).map(this::toSet).ifPresent(task::setAdminGroups);
+
ofNullable(workItem.getParameters().get(EXCLUDED_OWNER_ID)).map(String.class::cast).map(this::toSet).ifPresent(task::setExcludedUsers);
+
+
ofNullable(workItem.getParameters().get(NOT_STARTED_NOTIFY)).map(String.class::cast).map(DeadlineHelper::parseDeadlines).ifPresent(task::setNotStartedDeadlines);
+
ofNullable(workItem.getParameters().get(NOT_STARTED_REASSIGN)).map(String.class::cast).map(DeadlineHelper::parseReassignments).ifPresent(task::setNotStartedReassignments);
+
ofNullable(workItem.getParameters().get(NOT_COMPLETED_NOTIFY)).map(String.class::cast).map(DeadlineHelper::parseDeadlines).ifPresent(task::setNotCompletedDeadlines);
+
ofNullable(workItem.getParameters().get(NOT_COMPLETED_REASSIGN)).map(String.class::cast).map(DeadlineHelper::parseReassignments).ifPresent(task::setNotCompletedReassignments);
+
+ task.initialize(emptyMap(),
IdentityProviders.of(WORKFLOW_ENGINE_USER));
+ });
if (workItem instanceof InternalKogitoWorkItem ikw) {
ikw.setExternalReferenceId(instance.getId());
diff --git a/jbpm/jbpm-usertask/pom.xml b/jbpm/jbpm-usertask/pom.xml
index 9e1072a02f..dcaf179947 100644
--- a/jbpm/jbpm-usertask/pom.xml
+++ b/jbpm/jbpm-usertask/pom.xml
@@ -69,6 +69,11 @@ under the License.
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<dependencyManagement>
diff --git
a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstance.java
b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstance.java
index 8ba6039156..6a3eea5e27 100644
---
a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstance.java
+++
b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstance.java
@@ -119,6 +119,9 @@ public class DefaultUserTaskInstance implements
UserTaskInstance {
private Map<String, Reassignment> notCompletedReassignmentsTimers;
+ // preventing unnecessary database updates during batch operations
+ private boolean batchOperation = false;
+
public DefaultUserTaskInstance() {
this.inputs = new HashMap<>();
this.outputs = new HashMap<>();
@@ -262,23 +265,40 @@ public class DefaultUserTaskInstance implements
UserTaskInstance {
@Override
public void transition(String transitionId, Map<String, Object> data,
IdentityProvider identity) {
- Optional<UserTaskTransitionToken> next =
Optional.of(this.userTaskLifeCycle.newTransitionToken(transitionId, this,
data));
- while (next.isPresent()) {
- UserTaskTransitionToken transition = next.get();
- next = this.userTaskLifeCycle.transition(this, transition,
identity);
- this.status = transition.target();
- this.userTaskEventSupport.fireOneUserTaskStateChange(this,
transition.source(), transition.target());
+ batchUpdate(instance -> {
+ Optional<UserTaskTransitionToken> next =
Optional.of(this.userTaskLifeCycle.newTransitionToken(transitionId, instance,
data));
+ while (next.isPresent()) {
+ UserTaskTransitionToken transition = next.get();
+ next = this.userTaskLifeCycle.transition(instance, transition,
identity);
+ instance.status = transition.target();
+
instance.userTaskEventSupport.fireOneUserTaskStateChange(instance,
transition.source(), transition.target());
+ }
+ });
+ }
+
+ public void batchUpdate(Consumer<DefaultUserTaskInstance> batch) {
+ this.batchOperation = true;
+ try {
+ batch.accept(this);
+ } finally {
+ this.batchOperation = false;
}
this.updatePersistenceOrRemove();
}
private void updatePersistence() {
+ if (this.batchOperation) {
+ return;
+ }
if (this.instances != null) {
this.instances.update(this);
}
}
private void updatePersistenceOrRemove() {
+ if (this.batchOperation) {
+ return;
+ }
if (this.status.isTerminate()) {
this.instances.remove(this);
} else {
@@ -792,5 +812,4 @@ public class DefaultUserTaskInstance implements
UserTaskInstance {
return "DefaultUserTaskInstance [id=" + id + ", status=" + status + ",
actualOwner=" + actualOwner + ", taskName=" + taskName + ", taskDescription=" +
taskDescription + ", taskPriority="
+ taskPriority + ", slaDueDate=" + slaDueDate + "]";
}
-
}
diff --git
a/jbpm/jbpm-usertask/src/test/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstanceTest.java
b/jbpm/jbpm-usertask/src/test/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstanceTest.java
new file mode 100644
index 0000000000..89a695533b
--- /dev/null
+++
b/jbpm/jbpm-usertask/src/test/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstanceTest.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.kie.kogito.usertask.impl;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.kie.kogito.usertask.UserTaskInstances;
+import org.kie.kogito.usertask.lifecycle.UserTaskLifeCycle;
+import org.kie.kogito.usertask.lifecycle.UserTaskState;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+public class DefaultUserTaskInstanceTest {
+
+ private UserTaskInstances instances;
+ private DefaultUserTaskInstance userTaskInstance;
+
+ @BeforeEach
+ public void setup() {
+ instances = mock(UserTaskInstances.class);
+ userTaskInstance = new DefaultUserTaskInstance();
+ userTaskInstance.setInstances(instances);
+ userTaskInstance.setUserTaskLifeCycle(mock(UserTaskLifeCycle.class));
+ }
+
+ @Test
+ public void testBatchUpdateCallsUpdateOnce() {
+ userTaskInstance.batchUpdate(task -> {
+ task.setTaskName("Test Task");
+ task.setTaskDescription("Test Description");
+ task.setTaskPriority("High");
+ task.setActualOwner("testUser");
+ });
+
+ verify(instances, times(1)).update(userTaskInstance);
+ }
+
+ @Test
+ public void testNonBatchUpdateCallsUpdateMultipleTimes() {
+ userTaskInstance.setTaskName("Test Task");
+ userTaskInstance.setTaskDescription("Test Description");
+ userTaskInstance.setTaskPriority("High");
+ userTaskInstance.setActualOwner("testUser");
+
+ verify(instances, times(4)).update(userTaskInstance);
+ }
+
+ @Test
+ public void testBatchUpdateCallsRemoveWhenTerminated() {
+ userTaskInstance.setStatus(UserTaskState.of("Completed",
UserTaskState.TerminationType.COMPLETED));
+
+ userTaskInstance.batchUpdate(task -> {
+ task.setTaskName("Completed Task");
+ });
+
+ verify(instances, times(1)).remove(userTaskInstance);
+ verify(instances, never()).update(userTaskInstance);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]