Async, Triggerable, and basic task design

Maybe this is answered elsewhere. I spent a good amount of time looking and didn’t see what I wanted which was a way simpler explanation of what these things mean.

here’s my use case. its common. i’m sure a thousand people have solved it.

I have a serviceTask written in Java. It does something that could take a while. I want to do that task asynchronously in the meaning that it runs execute, returns, but does not complete the task and move on in the process until I explicitly say the task is complete. I have been told that even when using asynchronous tasks when execute returns the process continues, meaning that the task may be executed by flowable asynchronously, the task itself is not asynchronous.

my hope was to have some other mechanism to trigger the continuation of the process outside of whats automated by flowable. And I’d prefer to do it in a way that doesn’t require many tasks to do essentially just waiting for notification. So if that’s possible it would be ideal because it makes using our task components easier with less boilerplate in the workflow just to access remote systems.

How would I do this? I think my understanding (or lack of) of two terms is at the heart of this problem. Async and Triggerable.

from my searches it looks like there are ways to trigger actions on tasks but if they already complete when execute returns then whats the point?

I would appreciate any help. I’m sure this has been done successfully by many people.

Thanks,
Jonathan

Hey @Jnight82,

Do you want your task to continue in the same transaction as when it was executed or do you want your task to send something to the other service in a blocking way and then continue the process instance in a new transaction?

Not sure if you have already seen the blog posts: Demystifying the Asynchronous Flag and Demystifying the Asynchronous Flag (II).

The triggerable is explained in How to use triggerable and here in the documentation.

From my understanding the triggerable Service task is exactly what you are looking for. When you make your service task triggerable, it will execute, but it will not complete. It will wait for a trigger signal from the outside in order to continue the process execution. In a way when you make your service task triggerable it will become a wait state. Keep in mind that this is only for service tasks with a class or delegateExpression JavaDelegates and not for ServiceTasks that use an expression.

Depending on how you are communicating with your other services you might want to look into the Event Registry as well.

Cheers,
Filip

1 Like

ok that does sound exactly like what I’m looking for. We have a calls extending java delegate, and in the execute method it makes a call to a remote system. I don’t want the flowable thread to wait for that to complete as it could take hours, so I’d want to just fire off the request to the external system and let execute() return. I’m assuming if the process is in a wait state its not using any threads, and the state is just recorded in a table somewhere.

–ok more info. that is NOT what happened. as I set the serviceTask to be async/triggerable and fire off the long running work in a thread, and execute(DelegateExecution execution) returns, but the workflow just continues on.

when the thread ends i call runtimeService.triggerAsync(instanceId) to try and get the process to continue but i think its already completed by then. Is there something i need to do to get the workflow to halt and wait for me to programatically continue it?

How does your delegate look like? Does it implement the TriggerableActivityBehavior and have you set triggerable to true in the model?

yes, it does both. in the execute method I kick of a thread, let the execute method return. things halt as I expect. Then the once thread completes it has a callback mechanism that gets the DelegateExecution id and calls runtimeService.triggerAsync with that id. it does get into the trigger method, but from here I’m stumped because it won’t cause the process instance to continue onto the next step int he bpmn. it just never goes any further.

I have tried other things too. I tried suspending the process instance inside the deletages execute method and then activating it once the thread is done. this seemed promising but I found out that suspending the process will not record that it completed the task you suspending it in, even though the thread continues and the execute method returns. So when I call activate it ends up executing the same task again. I tried to get around this by setting some variable on the delegate execution so when it executes the task the second time it knows it can just skip the part about kicking off the worker thread. This made sense to me, even though it presented some potential race conditions. But for some reason I was getting multiple executions of the task, way more than what i excepted (which was just twice, once to kick off the thread, and he second on the activate call)

so then i went back to the triggerable approach one last time and it seemed to work. I’m not sure what was up with the first time trying it, maybe i had some conflicting workflows deployed that cause the trigger call to do the wrong thing.

One new thing I noticed though, is that the delegateExecution reference is not safe in memory. I don’t know how flowable does it but I tried to keep a reference to the original execution object in the thread thats off doing the long standing work, and when it comes back that things has been modified by other process instances. So i had to save off the fields i needed to enact the trigger call, namely the processInstanceId and the executionId. It seems to be working now. That last bit took me a hot minute to debug.

Glad to read that things worked out. But I do wonder about that last comment. Can you elaborate what you mean with that it is not safe in memory? The way it works: when an execution arrives at a certain activity in the process, that execution won’t be used for executing anything else. So passing the executionId back when triggering is enough to pinpoint exactly which step to continue. If there are multiple process instances, they will have each a distinct set of executions.