Conclusions from OutOfMemory - design suggestions

Hello,

We are using Flowable 6.8.0. Last week, we experienced an Out Of Memory problem. After investigation, we discovered the issue in the following code:

Flowable.getInstance().historyService().createHistoricProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();

The issue was caused by the processInstanceId parameter being null, which resulted in loading over 6 million rows from the database.

Of course, passing processInstanceId == null was an error in our source code. However, in MyBatis, a null value is interpreted as simply ignoring the condition:

<sql id="commonInstanceQuerySql">
  <if test="processInstanceId != null">
    ${queryTablePrefix}PROC_INST_ID_ = #{processInstanceId, jdbcType=NVARCHAR}
  </if>
</sql>

Questions:

  1. Should methods like processInstanceId allow null as a parameter? If not, perhaps some validation should be added. If yes, maybe you should keep null in value holder, so MyBatis would add the condition PROC_INST_ID_ = null instead of ignoring it.

  2. Let’s look at the method org.flowable.common.engine.impl.query.AbstractQuery.executeSingleResult(CommandContext) which is implemented like this:

public U executeSingleResult(CommandContext commandContext) {
    List<U> results = executeList(commandContext);
    if (results.size() == 1) {
        return results.get(0);
    } else if (results.size() > 1) {
        throw new FlowableException("Query returned " + results.size() + " results instead of max 1");
    }
    return null;
}

The above implementation could be improved to be faster and to avoid OutOfMemory errors. Simply replace

List<U> results = executeList(commandContext);

with

List<U> results = listPage(0, 2);

Hey @MirekSz,

For this ideally there would be a validation check in query to avoid passing null there. Would you be interested in doing such a contribution?
I don’t think that using a value holder to allow doing PROC_INST_ID_ is null would make much sense since that is not possible here, every process instance has an id.

Your other suggestion would also be an option, but I would say from a user point of view a validation is a bit better, since the error message “Query returned 2 results instead of max 1” would not really be clear why that happened.

Cheers,
Filip

  1. Maybe I’ll try to submit an MR on GitHub over the weekend, but implementing backward compatibility will be a challenge.

  2. Simply having an exception like "Query returned 2 results instead of max 1" is not very informative about why it happened.

In my situation, even if I don’t get an OOM, Flowable will throw an exception like this:

throw new FlowableException("Query returned 654234921 results instead of max 1");

which is also not ideal. Maybe, after optimization (using listPage(0, 2)), the exception should look more like:

throw new FlowableException(
  "Query returned more results instead of max 1. Current criteria: {processInstanceId=12, processDefinitionCode='FSAL', startDate='2025-10-14 13:22'}"
);

This way, it would be much clearer what search criteria led to the issue.

1 Like