I am quite a fan of BPM concepts and since for the time JBPM is the most know implementation which I have used already in several projects I am stuck with it. Yet it’s one of the more complex-inefficient models I have seen. Enough cause to loose a few days.

The context

The application was tested with a few tens of workflows and works reasonable. Increasing the number of workflows by an order of ten and you have long waiting minutes ahead just for the simple task of finding the TaskInstances.

Here is the code:

GetTaskListCommand getTaskListCommand = new GetTaskListCommand(new String[] { actorId });
log.info("Here p1!");
List tasks = (List) getTaskListCommand.execute(jbpmContext);
log.info("Here p2!");

Using this basic measuring technique here is what you get (almost 10 minutes for 346 TaskInstances):

15:30:05,821 INFO  [WorkflowDashboardService] Here p1!
15:30:10,812 WARN  [ProxyWarnLog] Narrowing proxy to class org.jbpm.graph.node.TaskNode - this operation breaks ==
15:30:12,834 WARN  [ProxyWarnLog] Narrowing proxy to class org.jbpm.graph.node.TaskNode - this operation breaks ==
15:30:19,557 WARN  [ProxyWarnLog] Narrowing proxy to class org.jbpm.graph.node.TaskNode - this operation breaks ==
15:30:53,912 WARN  [ProxyWarnLog] Narrowing proxy to class org.jbpm.graph.node.TaskNode - this operation breaks ==
15:39:21,710 INFO  [WorkflowDashboardService] Here p2!
15:39:21,710 INFO  [WorkflowDashboardService] Results size: 346

Why this happens? Very simple. Because even if the initial query is fast:

  <query name="TaskMgmtSession.findTaskInstancesByActorId">
    <![CDATA[
      select ti
      from org.jbpm.taskmgmt.exe.TaskInstance as ti
      where ti.actorId = :actorId
        and ti.isSuspended != true
        and ti.isOpen = true
    ]]>
  </query>

the GetTaskListCommand does a retrieveTaskInstanceDetails for each element in the list retrieving everything again and again. Standard hibernate behaviour some might say.

So what’s the solution?

The solution is to write your own hibernate query and get the results much faster:

Session session = jbpmContext.getSession();
log.info("Here p1!");
List resultsRaw = new ArrayList();
//reimplement jbpmContext.getTaskList(actor)
Query q = session.createQuery("select ti.id, ti.name, ti.description, ti.priority, ti.dueDate, ti.create, " +
        "ti.token.processInstance.id, ti.token.processInstance.start, " +
        "ti.token.processInstance.version, ti.task.taskNode.id, ti.token.processInstance.processDefinition.name " +
        "from org.jbpm.taskmgmt.exe.TaskInstance as ti " +
        "where ti.actorId = :actorId and ti.isSuspended != true and ti.isOpen = true");
q.setParameter("actorId", actorId);
resultsRaw.addAll(q.list());

//reimplement jbpmContext.getGroupTaskList
Query qG = session.createQuery("select distinct ti.id, ti.name, ti.description, ti.priority, ti.dueDate, ti.create, " +
        "ti.token.processInstance.id, ti.token.processInstance.start, " +
        "ti.token.processInstance.version, ti.task.taskNode.id, ti.token.processInstance.processDefinition.name, " +
        ti.token.id and si.name = :variableName) " +
        "from org.jbpm.taskmgmt.exe.PooledActor pooledActor join pooledActor.taskInstances ti " +
        "where pooledActor.actorId = :actorId and ti.actorId is null and ti.isSuspended != true and ti.isOpen = true");
qG.setParameter("actorId", actorId);
resultsRaw.addAll(qG.list());
log.info("Here p2!");

And you will get results similar to:

15:18:55,547 INFO  [WorkflowDashboardService] task=list
15:18:55,552 INFO  [WorkflowDashboardService] Here p1!
15:19:01,093 INFO  [WorkflowDashboardService] Here p2!
15:19:01,093 INFO  [WorkflowDashboardService] Results size: 346

Getting from 10 minutes to 6 seconds is 100 times faster in my book. So the conclusion is if you want to do some volume work with JBPM you might need to spend some time to optimise your the code and stop believing all the propaganda.

Comments:

JBPM optimization - Java Tutorial -

[…] is a good tutorial on JBPM optimization: The application was tested with a few tens of workflows and works reasonable. […]