Monolith process to micro service (HELP)

Hi,

I’m evaluating the Camunda compensation mechanism.

My goal is to replace my monolith Order process with 2PC to micro service with saga pattern (see avoiding-the-bpm-monolith-when-using-bounded-contexts and event-command-transformation-in-microservice-architectures-and-ddd).

In the following exemple extracted from bernd ruecker blog, i can have an Order with Items from multi Supplier, so the “Ship goods” service will be a multi-instance one (by Supplier) and i have a “Received Task” when the micro service (Supplier) finish the shipping. When error occur in the Supplier micro service, the received task is in error and a compensation must be done for all Supplier.

I have the following process, the tasks are not well named regarding to the Bern Ruecker sample, but the goal is the same:

The sub process named “Second step” is a multi instance one (loop cardinality = 5), with cancellation and compensation.
When a cancellation is throw, i’m expected to see compensation for all completed instances (of the sub process), but i only see compensation for the instance throwing the cancellation.

According to the documentation:

Note: In case a cancel boundary event is placed on a transaction subprocess with multi instance characteristics, if one instance triggers cancellation, the boundary event cancels all instances.

The documentation tell nothing about compensation for other completed instances. How to do it ? Why camunda don’t compensate all completed instances ? It’s a missing feature, a bug or a incomprehension of myself ?

I have update my use case. Please help with transformation from monolith process to micro service.

Any ideas ? I’m stuck with this

To me - this makes a lot of sense. The instance that triggers the compensation should be the only one that is compensated. the fact the this compensation is triggered by an interrupting cancel event happens to cancel the remaining instances is a function of the boundary event rather than the compensation itself.

If your goal is the compensate all instances when if a single instance triggers a compensation there might be a better way of doing it. I’ll see what i can come up with.

Thx for the reply.

If your goal is the compensate all instances when if a single instance triggers a compensation there might be a better way of doing it. I’ll see what i can come up with.

My goal is at best to compensate only completed instance in the case of a multi transaction sub process.

This would do the trick:

https://cawemo.com/shares/400f636c-f7e7-4338-b346-65119ab3771a

Let me know if you have any questions.

I wondering if the signal event will be caught by other process instances ? If yes, it’s a big deal :D. I only want the multi instance of the current processes to be compensate. Not for all process instances.

In the example above i’m using an expression as the signal name to ensure that for each instance the signal is unique.

But this of course isn’t obvious from the visual representation. :slight_smile:

In your example, if before the sub process we add a service task with compensation, and the sub process failed, signal is launch, all multi instance are compensate but the task before will not be ? Can you confirm ?

It will only activate compensation tasks within the scope of the sub process. If you had and another task before the sub process it would not be triggered.

Can we catch the signal outside the sub process and compensate the first task. Or this approch is restricted to sub process. If it is, is not the right way for us

If you’d like to trigger other compensations on a global scope it’s a pretty simple change to the model

Edit : I’ve added this change to the Cawemo model in the link above

1 Like

I have strange behavior with this method. My process is the following:

Logs:

(first step) => log ok
(second step) => log ok n°0
(back-end | ShipGoodAdapter) call (Supplier) micro service n° 0
(second step) => log ok n°1
(back-end | ShipGoodAdapter) call (Supplier) micro service n° 1
(second step) => log ok n°2
(back-end | ShipGoodAdapter) call (Supplier) micro service n° 2
(second step) => log ok n°3
(back-end | ShipGoodAdapter) call (Supplier) micro service n° 3
(second step) => log ok n°4
(back-end | ShipGoodAdapter) call (Supplier) micro service n° 4
(second step) => after received task with success:true for n°0
(second step) => after received task with success:true for n°1
(second step) => after received task with success:true for n°2
-------------------
(back-end) send step 2 ship good compensation !!!!
-------------------
(second step) => after received task with success:false for n°3
(second step) => event sub process compensation from signal n°4
(second step) => log compensation n°4
 before all compensation
(first step) => log compensation
(second step) => event sub process compensation from globall compensation n°null
(second step) => event sub process compensation from globall compensation n°null
(second step) => log compensation n°null
(second step) => ship compensation n°null
(second step) => log compensation n°null
(second step) => ship compensation n°null
(second step) => event sub process compensation from globall compensation n°null
(second step) => log compensation n°null
(second step) => ship compensation n°null
(second step) => event sub process compensation from globall compensation n°null
(second step) => log compensation n°null
(second step) => ship compensation n°null
(second step) => event sub process compensation from globall compensation n°null
(second step) => log compensation n°null
(second step) => ship compensation n°null
after all compensation

Signal occur for the item n°3 of the sub process (no compensation is launched) but compensation only for n°4 that is not yet finished. Items n°1, 2, 3 are completed and no compensate :thinking:. Not what i want but it’s understandable.

Plus, the non interrupting signal boundary event is not fired, compensate is fired by the the last compensation of the process, between the log “before all compensation” and “before all compensation”. I’m not sure about understanding the expected behavior with the non interrupting signal boundary event on a multi-instance subprocess with event sub process and compensation…

Plus the compensation order is strange, the first step run before second step (sub process). From what I understand from the documentation, the compensation must be done in the reverse order. Why all the items of the second step are not compensate before the first one ?

I have the project base on the camunda-unittest project, if you need it @Niall .

Can you upload your bpmn process model?

You mean the java project ? Have you a repository, file sharing ?

Just the BPMN file itself should be enough for now.
If you want to put your project somewhere on github that would also be handy.

testProcess4.bpmn (31.3 KB)

public class SmartShipGoodService extends PublishSubscribeAdapter {

    private static Integer count = 0;

    public void execute(final ActivityExecution context) throws Exception {

        addMessageSubscription(context, "Message_shipping");

        System.out.println("(back-end | ShipGoodAdapter) call (Supplier) micro service n° "+context.getVariable("loopCounter"));
    }
}

public class PublishSubscribeAdapter extends AbstractBpmnActivityBehavior {
    
  public void signal(ActivityExecution execution, String signalName, Object signalData) throws Exception {
    leave(execution);
  }
  
  protected void addMessageSubscription(final ActivityExecution context, String eventName) {
    ExecutionEntity executionEntity = (ExecutionEntity)context;
    EventSubscriptionEntity eventSubscriptionEntity = new EventSubscriptionEntity(executionEntity, EventType.MESSAGE);
    eventSubscriptionEntity.setEventName(eventName);
    eventSubscriptionEntity.setActivity(executionEntity.getActivity());
    eventSubscriptionEntity.insert();
  }

}

@Test
  @Deployment(resources = {"testProcess4.bpmn"})
  public void shouldExecuteProcess4() {
    ProcessInstance processInstance = runtimeService().startProcessInstanceByKey("testProcess4");

    List<Execution> messagesShipping = getExecutions(processInstance); // cardinality is 5 for the subprocess
    for (int i = 0; i < messagesShipping.size(); i++) {
      Map<String, Object> variables = new HashMap<String, Object>();
      if (i == 3) { // set i == 30 to avoid sub process signal and arrive to the global compensation
        System.out.println("-------------------");
        System.out.println("(back-end) send step 2 ship good compensation !!!!");
        System.out.println("-------------------");
        variables.put("success", false);
      } else {
        variables.put("success", true);
      }
      // compensate received message added programmatically be SmartShipGoodService
      // some message sending can failed due to sub process signal launch and the compensation already append
      runtimeService().messageEventReceived("Message_shipping", messagesShipping.get(i).getId(), variables);
    }
  }

  private List<Execution> getExecutions(ProcessInstance processInstance) {
    return executionQuery().processInstanceId(processInstance.getId()).messageEventSubscriptionName("Message_shipping").list();
  }

The project is here => https://github.com/jdupont22/camunda-engine-unittest-compensation.git

Use

  @Test
  @Deployment(resources = {"testProcess4.bpmn"})
  public void shouldExecuteProcess4() {

What are your expectations of this construct - seems a bit weird to me

Also - the java class you’re running what are you trying to accomplish with that?

-Niall

When all is good in the subprocess and i want compensate later, with the last step, outside of the subprocess. Imagine the second step is a call to one micro service, but i have 5 like that in my real case. Also, i realized that i need to order myself the compensation inside the subprocess with an event subprocess if not, the compensation was not well ordered.