cancel
Showing results for 
Search instead for 
Did you mean: 

Compensation in Parallel Tasks

gokceng1
Champ in-the-making
Champ in-the-making
I couldn't understand compensation in parallel execution. You can see the here here and XML here:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://sourceforge.net/bpmn/definitions/_1354022728531" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:yaoqiang="http://bpmn.sourceforge.net" exporter="Yaoqiang BPMN Editor" exporterVersion="2.1.11" expressionLanguage="http://www.w3.org/1999/XPath" id="_1354022728531" name="" targetNamespace="http://sourceforge.net/bpmn/definitions/_1354022728531" typeLanguage="http://www.w3.org/2001/XMLSchema" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://bpmn.sourceforge.net/schemas/BPMN20.xsd">
  <process id="PROCESS_1" isClosed="false" isExecutable="true" processType="None">
    <startEvent id="_2" isInterrupting="true" name="Start Event" parallelMultiple="false">
      <outgoing>_4</outgoing>
    </startEvent>
    <scriptTask completionQuantity="1" id="_3" isForCompensation="false" name="Script Task1" scriptFormat="groovy" startQuantity="1">
      <incoming>_4</incoming>
      <outgoing>_36</outgoing>
      <script><![CDATA[out:println execution.getCurrentActivityName();]]></script>
    </scriptTask>
    <sequenceFlow id="_4" sourceRef="_2" targetRef="_3"/>
    <parallelGateway gatewayDirection="Unspecified" id="_5" name="Parallel Gateway">
      <incoming>_37</incoming>
      <outgoing>_8</outgoing>
      <outgoing>_10</outgoing>
    </parallelGateway>
    <scriptTask completionQuantity="1" id="_7" isForCompensation="false" name="Script Task2" scriptFormat="groovy" startQuantity="1">
      <incoming>_8</incoming>
      <outgoing>_12</outgoing>
      <script><![CDATA[out:println execution.getCurrentActivityName();]]></script>
    </scriptTask>
    <sequenceFlow id="_8" sourceRef="_5" targetRef="_7"/>
    <scriptTask completionQuantity="1" id="_9" isForCompensation="false" name="Script Task3" scriptFormat="groovy" startQuantity="1">
      <incoming>_10</incoming>
      <outgoing>_16</outgoing>
      <script><![CDATA[out:println execution.getCurrentActivityName();]]></script>
    </scriptTask>
    <sequenceFlow id="_10" sourceRef="_5" targetRef="_9"/>
    <intermediateThrowEvent id="_11" name="Intermediate Throw Event">
      <incoming>_12</incoming>
      <outgoing>_14</outgoing>
      <compensateEventDefinition id="_11_ED_1" waitForCompletion="true"/>
    </intermediateThrowEvent>
    <sequenceFlow id="_12" sourceRef="_7" targetRef="_11"/>
    <parallelGateway gatewayDirection="Unspecified" id="_13" name="Parallel Gateway">
      <incoming>_14</incoming>
      <incoming>_17</incoming>
      <outgoing>_19</outgoing>
    </parallelGateway>
    <sequenceFlow id="_14" sourceRef="_11" targetRef="_13"/>
    <intermediateThrowEvent id="_15" name="Intermediate Throw Event">
      <incoming>_16</incoming>
      <outgoing>_17</outgoing>
      <compensateEventDefinition id="_15_ED_1" waitForCompletion="true"/>
    </intermediateThrowEvent>
    <sequenceFlow id="_16" sourceRef="_9" targetRef="_15"/>
    <sequenceFlow id="_17" sourceRef="_15" targetRef="_13"/>
    <scriptTask completionQuantity="1" id="_18" isForCompensation="false" name="Script Task4" scriptFormat="groovy" startQuantity="1">
      <incoming>_19</incoming>
      <outgoing>_21</outgoing>
      <script><![CDATA[out:println execution.getCurrentActivityName();]]></script>
    </scriptTask>
    <sequenceFlow id="_19" sourceRef="_13" targetRef="_18"/>
    <intermediateThrowEvent id="_20" name="Intermediate Throw Event">
      <incoming>_21</incoming>
      <outgoing>_23</outgoing>
      <compensateEventDefinition id="_20_ED_1" waitForCompletion="true"/>
    </intermediateThrowEvent>
    <sequenceFlow id="_21" sourceRef="_18" targetRef="_20"/>
    <endEvent id="_22" name="End Event">
      <incoming>_23</incoming>
    </endEvent>
    <sequenceFlow id="_23" sourceRef="_20" targetRef="_22"/>
    <boundaryEvent attachedToRef="_3" cancelActivity="true" id="_24" name="Boundary Event" parallelMultiple="false">
      <compensateEventDefinition id="_24_ED_1" waitForCompletion="true"/>
    </boundaryEvent>
    <boundaryEvent attachedToRef="_7" cancelActivity="true" id="_25" name="Boundary Event" parallelMultiple="false">
      <compensateEventDefinition id="_25_ED_1" waitForCompletion="true"/>
    </boundaryEvent>
    <boundaryEvent attachedToRef="_9" cancelActivity="true" id="_26" name="Boundary Event" parallelMultiple="false">
      <compensateEventDefinition id="_26_ED_1" waitForCompletion="true"/>
    </boundaryEvent>
    <boundaryEvent attachedToRef="_18" cancelActivity="true" id="_27" name="Boundary Event" parallelMultiple="false">
      <compensateEventDefinition id="_27_ED_1" waitForCompletion="true"/>
    </boundaryEvent>
    <scriptTask completionQuantity="1" id="_28" isForCompensation="true" name="Compensation Script Task2" scriptFormat="groovy" startQuantity="1">
      <script><![CDATA[out:println execution.getCurrentActivityName();]]></script>
    </scriptTask>
    <scriptTask completionQuantity="1" id="_30" isForCompensation="true" name="Compensation Script Task3" scriptFormat="groovy" startQuantity="1">
      <script><![CDATA[out:println execution.getCurrentActivityName();]]></script>
    </scriptTask>
    <scriptTask completionQuantity="1" id="_32" isForCompensation="true" name="Compensation Script Task4" scriptFormat="groovy" startQuantity="1">
      <script><![CDATA[out:println execution.getCurrentActivityName();]]></script>
    </scriptTask>
    <scriptTask completionQuantity="1" id="_34" isForCompensation="true" name="Compensation Script Task1" scriptFormat="groovy" startQuantity="1">
      <script><![CDATA[out:println execution.getCurrentActivityName();]]></script>
    </scriptTask>
    <intermediateThrowEvent id="_6" name="Intermediate Throw Event">
      <incoming>_36</incoming>
      <outgoing>_37</outgoing>
      <compensateEventDefinition id="_6_ED_1" waitForCompletion="true"/>
    </intermediateThrowEvent>
    <sequenceFlow id="_36" sourceRef="_3" targetRef="_6"/>
    <sequenceFlow id="_37" sourceRef="_6" targetRef="_5"/>
    <association associationDirection="One" id="_29" sourceRef="_25" targetRef="_28"/>
    <association associationDirection="One" id="_31" sourceRef="_26" targetRef="_30"/>
    <association associationDirection="One" id="_33" sourceRef="_27" targetRef="_32"/>
    <association associationDirection="One" id="_35" sourceRef="_24" targetRef="_34"/>
  </process>
  <bpmndi:BPMNDiagram documentation="background=#FFFFFF;count=1;horizontalcount=1;orientation=0;width=597.6;height=842.4;imageableWidth=587.6;imageableHeight=832.4;imageableX=5.0;imageableY=5.0" id="Yaoqiang_Diagram-_1" name="New Diagram">
<!– Excluded –>
  </bpmndi:BPMNDiagram>
</definitions>

Execution order of this process:
Script Task1
Compensation Script Task1
Script Task2
Script Task3
Script Task4
Compensation Script Task4
Compensation Script Task3
Compensation Script Task2

What I didn't understand is why Intermediate Compensation Throw Events after Script 2 and Script 3 didn't cause trigger Compensation Script Task3, Compensation Script Task2?

In short, why does process wait for compensation event which is after Script Task 4?
12 REPLIES 12

jbarrez
Star Contributor
Star Contributor
The Activiti engine executes the steps not in parallel. Ie, it executes what it encounters first, also the order of sequence flow in the XML is respected. That is what you are seeing here.

gokceng1
Champ in-the-making
Champ in-the-making
I have no problem with that approach. What I couldn't understand is throwing a compensation event in a parallel line doesn't trigger anything. I expect it to trigger the same-line task's compensation listener at least.

For example throwing compensation event after Script Task2 or Script Task3 doesn't trigger Compensation Script Task3 or Compensation Script Task2 whatever the call order is. Why?

jbarrez
Star Contributor
Star Contributor
Why would they be catched by those compensation handlers? The compensation catch is on the scope of the script task. The script task is done when you arrive t the throw event step.

In the other case of execution (ie second throw should enable first catch), my reasoning of above matters: either the script task is already done or it is not yet reached.

If you want this to work, you need to introduce a bigger scope, by using eg. an embedded subprocess.

gokceng1
Champ in-the-making
Champ in-the-making
I'm not sure that I understood you correctly especially about scopes.
You can see my edits on image export of original process: image

1st and 4th items works same as what I expect on the other hand 2 and 3 doesn't work in that way(not working at all). And you say this behaviour is normal. I see 4 identical groups of items in process image.

The compensation catch is on the scope of the script task. The script task is done when you arrive t the throw event step.
What is the difference between 2/3 and 1/4? Script 1 or 4 is also done when I throw compensation event, but it triggers the listener on same task.

May be I really misunderstood or totally didn't understand your point. Could you explain it again?

jbarrez
Star Contributor
Star Contributor
Okay, I re-read everything and you are correct, the behaviour is not correct. Sorry for the confusion caused by me.

However, actually the reason why the first and the fourth one works is actually more by accident than anything else.
When a compensate intermediate event is thrown like here, it will query the database.
However, since you only have automatic steps here, the data is not yet in the database, and hence the query returns nothing. The reason it works for the first and the last one, is because the event subscriptions are cached on the execution. The reason it fails twice, is because the parallel executions are new executions which don't have the cached versions.

I see two potential solutions:
- When doing a query, the engine should always look in the 'pending inserts'. This would be the correct solution, but I don't know the damages that might to do other existing code.
- For throwing events, we could check the execution tree upwards to find a cached version


I've sent an email to Daniel asking him to comment here, who wrote this functionality to see what his opinion is.

jbarrez
Star Contributor
Star Contributor
On the other hand, this line of code

  ExecutionEntity scopeExecution = ScopeUtil.findScopeExecutionForScope((ExecutionEntity)execution, (ActivityImpl)execution.getActivity());
   

Should return the correct execution … maybe there is a bug in there?

gokceng1
Champ in-the-making
Champ in-the-making
Thank you so much for quite clear explanation. We'll wait for Daniel's answer for this situation.

meyerd
Champ on-the-rise
Champ on-the-rise
Hi gokceng,

this should definately work as you describe. If it doesn't, it's a bug. Could you maybe provide a failing activiti unit testcase for your process?

jbarrez
Star Contributor
Star Contributor
@Dan: you can just copy and paste his process and run it (I would suggest using my unit test template ;-).