The Flow activity is used to configure parallel activity in BPEL processes. In theory, activities contained in two or more branches (sequence containers) inside a Flow activity are executed in parallel. However, some sections in the BPEL PM documentation raise some doubt: "By default, Oracle BPEL Process Manager executes in a single thread, executing the branches sequentially instead of in parallel". I am not sure exactly what this means. But it certainly suggests that what I assumed to be pure parallel branches are in fact activity sequences that are both carried out, but sequentially! Time to investigate…
In this article, we will go through a number of steps to ascertain what exactly the parallellism for the Flow activity is. Note that I did this research in the 11g TP4 release of the SOA Suite. I will repeat the analysis in the 10g stack – as some findings seem erroneous and are perhaps due to the status of the Technical Preview.
The basic BPEL process we look at (initially) does nothing much except initialize two variables, x and y, with a value of 1. Then a Flow contains two branches: one manipulating x using the current value of y and the other manipulating y using the current value of x. This allows us determine the order in which the branches and steps within the branches are executed.
- we start with a normal Flow with two branches. Each has two activities. Both branches act on the same pair of variables. However, from the end-result of the process, we can tell whether the activities were executed in parallel or sequentially
The response from the process (service):
This tells us that Y went first, then X, then Y again and finally X once more:
The audit trail confirms this:
We will find that normal, synchronous steps, are executed sequentially (one step at a time) (in a single thread); however, when one step is executed in one branch, it seems that the focus switches to the next branch where a step will be executed. it turns out that branch does not have to wait for another branch to finish – branches get a chance in turn to execute their next step (or so it seems). True parallellism would of course introduce issues with accessing the same BPEL variables – this access would then have to be synchronized introducing a world of potential issues. So this step by step execution is probably a very logical thing.
Long running step – an (asynchronous) Wait
When the first step suggests that branches with synchronous activities always execute sequentially – first one branch, executing until completely done and only then the other branch – we add an asynchronous step – a wait for a very small period; would that be enough to have the other branch start before the first one finishes?
I had expected to be able to conclude the following: The response from this process indicates that the X branch sat and waited while the Y branch executed both steps. The Wait did not prevent the Y branch to continue running.
However, that is not my finding! The audit trail for this process
This says: first update Y, then wait for 1 s, update X and only then continue with update Y for the second time. Depicting that visually:
I had expected to be able to conclude: "A wait operation is definitely a point where other branches can take over. Even if the WAIT is the next step for a branch tole th execute, other branches do not have to wait for it and continue while the wait takes place. Parallellism? Well – one branch does nothing (it waits) and another does something at same time. Hardly parallellism to very high degree – but useful all the same"
However: in the 11g TP4 that is not what I observe. Wait does not only stop processing in its own branch, but in other branches as well. And when the Wait completes, the branch continues to execute its next step as well prior to releasing its hold on the thread and allowing other branches to kick in. I hope this is an 11g TP anomaly. I will need to check this in 10g.
Synchronous call – will it block the other threads?
If there is still no true parallel execution – it is still one branch executing before the other one even starts – we can throw in an additional synchronous activity: a database adapter call and see whether that makes any difference.
The Database Adapter service (Stall) we introduce does nothing very useful: using dbms_lock.sleep it simply stalls the world for 10 seconds. Meanwhile you would expect the other branch to take its chance and continue its processing.
The debug output from this process looks like this:
The most important thing to conclude from this output is that the overall process took over 20 seconds instead of just over 10 which would have been the case if during a long running synchronous call another branch in the flow would execute a next step.
When a synchronous call is started, other branches have to wait for the call to complete before they get a chance to get on with their lifes.
At this point, it would be interesting to see whether the nonBlockingInvoke property actually does what the documentation suggests it does: in case of synchronous calls it spawns a new thread, which should allow ‘the other branch’ to complete
<property name=<span class="bold">"nonBlockingInvoke">true</span></property>
Note: this property is set in the bpel .xml file in the partnerLinkBinding element. (in 11g it is set from the Deployment Descriptor Dialog that can be opened from the context menu on the Partner Link for the Service in the BPEL diagram).
Unfortunately, our investigation does not prove any difference between the Database Service call with this property set or unset. I expected the branch to make the synchronous call to block other branches in case of this flag being false – which is indeed the case – and to allow parallel activity in other branches while the synchronous call is being made with the flag set to true; the latter is not the case (in TP4). Even when the flag is set, a synchronous call to a PL/SQL Procedure that sleeps for 10 seconds does not make way for another branch to execute a next step – not even in a separate thread as the documentation on this property seems to suggest.I have strong suspicion that the implementation in TP4 is not complete or correct. The setting seems not be stored for the partnerlink binding but for the process as a whole – which I think does not make sense. So we will wait for a more mature 11g release before drawing final conclusions. I have not yet tried this in 10.1.3.x but I assume that in those releases the flag will cause the synchronous calls to be ‘non blocking’ .
Turning the synchronous (Database) call into an asynchronous one
As a last resort I will try to create an asynchronous BPEL process that will do nothing but make the synchronous call to the PL/SQL procedure. From the context of the first BPEL process this call has now become asynchronous. It is my expectation that while the branch is awaiting the asynchronous response, another branch is allowed to execute a next step.
In the Composite editor this looks like this:
Run the FlowResearch revision 1.12 service:
The Audit trail from running the process. We can see how X starts to wait for the asynch response while Y continues on:
Visually this looks like:
it is clear that Y executes while X is waiting.
To really be sure that Y did not execute because it was it turn only, but would continue to execute while X was waiting for the asynch response, I have added an additional Wait step in Y branch.
The debug output:
The presentation of ‘wait has finished’ step is somewhat confusing: the wait that has finished is the wait 1s step in the Y branch, not the wait for the asynch response from the Stall Service.
And the visual flow:
Flow in BPEL (or at least Oracle BPEL PM) allows for parallel branches of activities. When executed, the branches in the Flow get their turn to execute the next step. Steps are not truly executed at the same time – which may be a blessing in disguise in situations where activities in various branches access the same variables. Even synchronous calls with nonBlockingInvoke set to true (however, I may have set that property in an incorrect way or TP4 of BPEL may not yet provide proper support for that property) block other activities from being processed. Only asynchronous activities such as Receive (wait for response to invoke of an Asynchronous Service) will allow other activities in other branches to be executed. I had expected this also to apply to Wait – but that turned out not be the case in TP4.
In a next article we will discuss a more advanced technique with Flows: a way of aligning two or more branches in a BPEL Flow through the use of Links, a little known BPEL feature to address complex concurrency challenges. Even with synchronous steps we should be able to specify dependencies between the branches and specific steps within the branches and enforce the order in which these steps can be executed.