Variable scoping issue with timed event within sub process

I have a process containing a sub-process in which I iterate over an array. In that sub process, I have a user task with a boundary timer event. In plain English: I want to send a reminder email when the manual task takes too long. See process definition below.

<definitions id="definitions"
             xmlns:flowable="http://flowable.org/bpmn"
             xmlns:bwise="http://bwise.com/bpmn"
             targetNamespace="http://bwise.com/bpmn"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">

    <process id="workflow_with_sub_process_and_timer" bwise:workflowType="test">
        <startEvent id="start" />
        <sequenceFlow id="flow1" sourceRef="start" targetRef="sub_process" />
        <subProcess id="sub_process">
            <multiInstanceLoopCharacteristics flowable:collection="${values}" flowable:elementVariable="eachValue"/>
            <startEvent id="sub_start"/>
            <sequenceFlow id="sub_flow1" sourceRef="sub_start" targetRef="serv_task" />
            <serviceTask id="serv_task"
                         flowable:expression="#{eachValue}"
                         flowable:resultVariable="subProcessVar"
                         flowable:useLocalScopeForResultVariable="true"/>
            <sequenceFlow id="sub_flow2" sourceRef="serv_task" targetRef="sub_assess" />
            <userTask id="sub_assess" flowable:assignee="joe"/>
            <sequenceFlow id="sub_flow4" sourceRef="sub_assess" targetRef="sub_end" />
            <endEvent id="sub_end" />

            <boundaryEvent id="reminderEmailEvent" cancelActivity="false" attachedToRef="sub_assess">
                <timerEventDefinition id="TimerEvent_Reminder">
                    <timeDuration>PT2S</timeDuration>
                </timerEventDefinition>
            </boundaryEvent>
            <sequenceFlow sourceRef="reminderEmailEvent" targetRef="reminderEmail" />
            <serviceTask id="reminderEmail" flowable:expression="#{result.add(subProcessVar)}">
            </serviceTask>
            <sequenceFlow sourceRef="reminderEmail" targetRef="endReminderEmail" />
            <endEvent id="endReminderEmail"/>

        </subProcess>
        <sequenceFlow id="flow2" sourceRef="sub_process" targetRef="end" />
        <endEvent id="end"/>
    </process>
</definitions>

I started the process with two values “v1” and “v2” in the “values” parameter. Now the problem is that the variable “subProcessVar” I assign in the “serv_task” serviceTask, cannot be reached from the “reminderEmail” serviceTask. It turns out that the execution used by the “reminderEmail” task is NOT a child execution of the one that carries the variables. I found this execution tree:

  • 4: the process
    – 16
    — 20: sub_process for elementVariable = “v1”
    ---- 26: sub_assess, having correct value “subProcessVar” = “v1”
    ---- 54: reminderEmail, having no value for “subProcessVar” (this is my problem)
    — 21: sub_process for elementVariable = “v2”
    ---- 29: sub_assess, having correct value “subProcessVar” = “v2”

Now the problem is that execution 54 is child of 20, and as such does not have a value for “subProcessVar”. Reason being that I had to use flowable:useLocalScopeForResultVariable=“true”, so the value is scoped to execution 26. If I don’t use local scope, then the value gets propagated all the way up to the highest level (the process instance), and subsequently gets overwritten by the second value “v2”.

Why, one might wonder, is execution 54 not a child of 26? I found the reason for that in the inline comments of BoundaryEventActivityBehavior.executeNonInterruptingBehavior(). With which I agree.

What I really would like to be able to do, is to assign the variable value to the sub-process scope, “20” in my example. However, I could not find a way to do that. Is there a way to do that?

Your analysis is correct (and detailed, appreciated).

We’re currently actually prototyping ideas to fix this (quite old) problem of variables that get lots due to usage of local scope.

For now, there is no other way than copying the variable to either the instance level (using a unique name) or fetching the variable from the other execution by querying the executions and finding the correct execution yourself.

I have indeed considered copying over the attributes from the userTask execution (to which the timer is attached) to the timer execution, and actually I found the place to implement that. However, I chose a different approach. My reasoning was that quite often, after using a multiInstanceLoopCharacteristics, you will want to bind variable values to the root execution of that iteration. So I made a custom tag like this:

<serviceTask id="serv_task"
                         flowable:expression="#{eachValue}"
                         flowable:resultVariable="subProcessVar"
                         mynamespace:useLoopScopeForResultVariable="true"/>

The “useLoopScopeForResultVariable” is slightly different to “useLocalScopeForResultVariable”: it puts the variable value on the execution for the loop, so that it can be used everywhere in executions that are child of this loop root execution. And it works quite well (except that I found this issue: ServiceTask does not read custom attribute from XML).

I would like to know more about your prototyping to fix this issue. Maybe I can align my fix to match your solution direction. Or maybe you like my solution…