cancel
Showing results for 
Search instead for 
Did you mean: 

Running workflows asynchronously

pmesmeur
Champ in-the-making
Champ in-the-making
Hello,

I would like to use Activiti to run workflows that possibly contain long service tasks (that may run for more than one minute). Such workflows would be called via:

ProcessEngine.getRuntimeService().startProcessInstanceByKey(…);


However, the method startProcessInstanceByKey() will return/leave only when the long service task will be competed, which is not acceptable. To avoid this unwanted behavior, I have to declare my service task as async. It is tested, it works fine.

Now, I realize that doing so, when I launch workflows having long running tasks, I exit asynchronously, while with workflows not having long running task, I exit quickly but synchronously.

So I am looking for a more consistent approach with which the answer will be always asynchronous. For the moment, I see the following possibilities:
  1. wrapping the call to startProcessInstanceByKey(…) in a thread (I don't like it very much)
  2.  
  3. making all the service tasks async; isn't it "too much"?
  4.  
  5. while parsing the BPMN, dynamically/programmatically adding a "dummy" async task after the workflow start point (and after each the user tasks)
I'm sharing my reflection in order to get possible ideas, remarks, feedback on this subjects. According to your experience about it, what do you think of my  propositions? do you have a preferred one? why? Any information is welcomed 😉

Another point I would like to clarify around async task: is the a difference regarding the database between sync and async task? If yes, do you think it has a high impact on the performance?

Regards,
Philippe
11 REPLIES 11

pmesmeur
Champ in-the-making
Champ in-the-making
I my 3rd point, a null timer would certainly solve my problem as well as a "dummy" empty async service task

pmsevestre
Champ in-the-making
Champ in-the-making
Instead of adding a dummy async task, what you can do is to create a custom parser handler that forces all "long running tasks" to be asynchronous.

That leads to the problem of how would you inform the parser when a task is "long" or not. One option is to elect one of the activities types for this specific purpose. The script task or system task are good candidates, since they´re the ones that usually contain custom logic that can take longer to execute.

Be aware though that this kind of implicit behaviour must be fully understood by the person who is designing the BPMN process, since it changes the transaction contract associated with the execution of consecutive activities.

martin_grofcik
Confirmed Champ
Confirmed Champ
Hi Philippe,

to answer your questions:
Is the a difference regarding the database between sync and async task?
Yes there is. (Async = create job and execute it)
If yes, do you think it has a high impact on the performance?
The performance impact is minimal with AsyncExecutor. DefaultJobExecutor performance impact is not huge too. (it really depends on your requirements)

Possible issue of your proposal is that "long" service call is executed in transaction too. (transaction can timeout during the service call).
Another issue is that one thread is blocked for +/- one minute during the service call.

I would prefer another approach.
  1. Just call the service and get response from the service that request was accepted. It should be fast.
  2. When service finishes its work it could send the signal (message) to the activiti engine to continue in the process execution.
Regards
Martin

Thank you Martin for this pertinent answer. I would like to clarify what you propose in order to be sure that I correctly get what you mean

As I understand, you propose that, for processing a long running service, I create a dedicated thread and temporarily suspend the execution of my workflow. In the new thread I do the (long) work and once it is completed, I send a message to the activiti engine in order it restarts the execution of the workflow. So I don't rely on async property anymore. Am I right?

If yes, I would like to know the cleaner way to suspend the service task? Has my long-running-service to extend ActivityBehavior instead of JavaDelegate? If yes once again, I'll have to make the BlueprintELResolver able to work with ActivityBehavior (not only with JavaDelegate)

Regards,
Philippe

pmesmeur
Champ in-the-making
Champ in-the-making
Thank you pmsevestre for your consideration.

"That leads to the problem of how would you inform the parser when a task is long or not": this is a point I already considered and which is easily doable, at least in the context of my project. To be short, in the function executeParse() of my own ServiceTaskParseHandler, I have this information:

<java>
public class MyOwnServiceTaskParseHandler extends ServiceTaskParseHandler {

    @Override
    protected void executeParse(BpmnParse bpmnParse, ServiceTask serviceTask) {
        ….
        boolean isAsync = hasTaskToBeAsync(serviceTask);
        bpmnParse.getCurrentActivity().setAsync(isAsync);
        …
    }
}
</java>

Doing that, I can easily make my long running tasks being async so startProcessInstanceByKey() will now be async, i.e. it will not wait for the end of my long running task before returning

Now, my main question the impact of making workflows "not having long running tasks" asynchronous too, in order they are treated consistently with workflows "having long running tasks"


"Be aware though that this kind of implicit behaviour must be fully understood by the person who is designing the BPMN process, since it changes the transaction contract associated with the execution of consecutive activities": one of my objectives is that the BPMN process designer is not impacted at all with any implementation detail. Can you please tell me more about the changes regarding "transaction contract associated with the execution of consecutive activities"?


One again, thank you very much

Philippe

trademak
Star Contributor
Star Contributor
Hi Philippe,

When you make any service / script task asynchronous, this means a job will be created in the Activiti database and it will be execute asynchronously in a different transaction. So if the task fails it will not rollback to previous state in the process instance, but it will retry the job.

Best regards,

pmesmeur
Champ in-the-making
Champ in-the-making
Now I'm convinced that I have to forget the async parameter, so I am studying another solution based on what I read on http://forums.activiti.org/content/java-service-task-passivate-wout-receive-task

First of all, my services do not implement JavaDelegate but ActivityBehavior (in fact they extend AbstractBpmnActivityBehavior)
In the "void execute(ActivityExecution execution)" method, two possibilities:
  1. The service is not a long running task
  2. [java]
    public void execute(ActivityExecution execution) throws Exception {
            doShortProcessing();
            this.leave(execution);
    }
    [/java]
  3. The service is a long running task
  4. [java]
    public void execute(ActivityExecution execution) throws Exception {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    doLongProcessing();
                    processEngine.getRuntimeService().signal(execution.getId());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        new Thread(r).start();
    }

    public void signal(ActivityExecution execution, String signalName, Object signalData) throws Exception {
        leave(execution);
    }
    [/java]
This solution seems to work fine. What do you think of it? Do you find any cons? Is it safer in a transnational point of view?

Best regards,
Philippe

trademak
Star Contributor
Star Contributor
Hi Philippe,

I don't see an issue with this approach. Should work fine if this is what you are looking for.

Best regards,

pmesmeur
Champ in-the-making
Champ in-the-making
Hi,

now that I have an acceptable solution to launch my long-running task asynchronously in a dedicated thread, I want - when an exception or a timeout occurs - to forward it to the activiti engine.

According to you, what is the best way a do this? I tried to do
[java]
processEngine.getRuntimeService().signalEventReceived("timeout", execution.getId());
[/java]
but unfortunately I did not succeed to subscribe pro grammatically to "timeout" signal

Thank you for your precious help
Philippe