Alex,
Regarding
syntax:
(1)
I do not see why not. An expression must evaluate to an integer.
(2)
Indeed.
(3)
Agree.
Regarding
function isCompleted():
The
proposal allows a completionCondition to be based on process variables (see
attribute expression). The idea of
function isCompleted('activity_name') is to be able to check
the state of any activity enclosed with a flow activity. The function does not
have to be specific to the flow activity. Activity <flow> with completion
condition is just an example, which motivates to define it. The execution of an
activity does not necessarily end modifying a process variable.
Regarding
the IgnoreFaults semantics:
I
agree that element <ignoreFaults> looks quite similar
to <faultHandlers> but has quite different behavior. It is indeed the
most controversial part of the proposal. Maybe we should start with the problem
we are trying to resolve.
The
specification is currently saying (see section 13.4): "... When a fault handler
is present, it is in charge of handling the fault. It might rethrow the same
fault or a different one, or it might handle the fault by performing cleanup and
allowing NORMAL PROCESSING to continue in the enclosing scope.... "
Let's
take a look at the following example - it is a "normal" flow activity (without a
completion condition).
<flow>
<scope name="A">
<faultHandlers>
<catch name="tns:my_fault">
<empty/>
</catch>
</faultHandlers>
<compensationHandler name="C">
...
</compensationHandler>
<sequence
name="X">
...
</sequence>
</scope>
<scope name
="B">
...
</scope>
</flow>
The
flow activity will complete as soon as all enclosing activities/scopes complete.
If fault "tns:my_fault" is thrown within sequence X it will cause the
termination of the sequence (all active enclosing activities) and will be
caught by the fault handler defined in the scope A. The fault handler will do
nothing, the scope A will be deemed as ended abnormally
and compensation handler C will not be installed. The flow element will
count that the scope completed and continue with normal processing. The
current semantics of the flow activity is that it waits for all concurrent
activities to complete. The completion also means that an enclosed
activity/scope may end abnormally. So I do not see how we can distinguish the
case of "N out of N" (current semantics of the flow activity) and "M out of
N".
Maybe
one way would be to use some process variables, for example (maybe this is also
your suggestion):
<variables>
<variable name="incompleteA"
type="xsd:boolean"/>
<variable name="incompleteB"
type="xsd:boolean"/>
<variables>
<assign>
incompleteA="false"
</assign>
<assign>
incompleteB="false"
</assign>
<flow>
<completionCondition>
...
</completionCondition>
<scope
name="A">
<faultHandlers>
<catch
name="tns:my_fault">
<assign>
incompleteA="true"
</assign>
<empty/>
</catch>
</faultHandlers>
<compensationHandler
name="C">
...
</compensationHandler>
<sequence
name="X">
...
</sequence>
</scope>
<scope name
="B">
....
</scope>
</flow>
So,
if sequence A experience some problem and fault tns:my_fault is thrown, fault
handler local to scope A will make sure that the flow activity may continue with
the normal processing and will at the same time identify that the scope A ended
abnormally. This approach is also possible but it requires a lot of
"programming" and users have to be aware of this kind of pattern. I prefer more
declarative approach even if it requires more time to give clear wording.
Regards,
Ivana
Hi,
Ivana, thanks for sending out this
proposal. I believe a number of us (including me) like the
<completionConditon> construct from your proposal high level speaking.
After reading the proposal, I guess we need to work on a number of
details. I would like to ask a few questions / mention a few points:
- Minor syntax and semantics questions:
- Do we want to allow expression for branch attribute? instead of just a
constant integer?
- We may need to some minor syntax adjustment for expression attribute
because we have passed Isseu 13 already.
- If answers to the above question are yes, then the condition syntax
may become a choice (xsd:choice) of the following two:
<condition
expressionLanguage="anyURI"?>boolean-expression</condition> <branchCondition
expressionLanguage="anyURI"?>integer-expression</condition>
- About completionConditionFailure: it looks like a nice idea so
far ...
- About the new "isCompleted()" function: I am not against
this function. But, I tend to think there are usually BPEL variables being
modifed as the execution result of a flow activity. (See the
train-or-plane-and-hotel example). Is it more typical to have
completionCondition to be evaluated on those variables?
Also, after the
completion of the whole flow, it is not uncommon for people to have logic to
find out which branch is finished (especially when "or" is involved in the
condition). The "isComplete()" function would be available only within the
completionCondition within the flow. People may get confused and attempt to
use the "isComplete()" function outside the flow. If the completionCondition
relies on variables, the same logic is applicable outside of the
flow.
- About the IgnoreFault semantics: I recall this ignoreFault idea
back to March F2F. With ignoreFault semantics, it essentially introduces two
brand new semantics.
- It turns <flow> into a semi-<scope> like construct
- It adds this new "ignoreFault" semantics: which catches certain fault
and the catching action will not cause any scope to be marked as faulted.
I tend to think this "ignoreFault" functionality has quite a
heavy weight impact to <flow> and fault handling picture in BPEL. If
there is an alternative way to achieve similar business logic, I would go
for the alternative way.
Quoted from Ivana's email: "Not all
activities must complete in order the enclosing flow activity to complete.
The way to identify that an enclosed activity did not complete is to
propagate faults."
I agree with the first sentence.
But, I am not sure that propagating fault is the only way to identify an
enclosed activity did not complete. I would rather use a scope as an
activity enclosed by flow. If we wish to suppress any minor fault which
happens during the execution of the scope, then we can just add a fault
handler. If the fault that happen, the scope will be marked as "faulted".
The completeConditon can identify the "incomplete" state of the
scope by two ways:
- completeCondition can be evaluated based on BPEL variables, which are
set to "incomplete" state by pre-flow initialization or related
fault-handler logic.
- branch-based condition will interprete a faulted scope as "incomplete"
branch and will not count them into the required N branches.
This will allow us to suppress minor fault with or without the
<completionCondition> construct. Please see the following example. It
is already legal and working today. It shows how to suppress "foo:barFault"
without the <completeCondition>
construct
<flow> <scope
name="A">
<faultHandler>
<catch faultName="foo:barFault" ... > ... </catch>
</faultHandler>
<sequence>
...
<invoke name="doA" ... /> ...
</sequence>
</scope> <scope name="B"> ... <!--
similar to A --> ... </scope>
</flow>
If the invoke of "doA" triggers
"foo:barFault", the scope "A" will be marked as faulted. On the other hand,
scope "B" will be allowed to continue and complete. After scope "B" is
completed, the compensation handler of scope "B" will be installed. And, the
overall execution of flow is still considered a normal completion.
I
hope I have illustrated we can achieve similar logic without introducing a
new "ignoreFault" construct and concept to BPEL.
What
"completionCondition" does should be just providing a way to pro-actively
cancel/terminate other flows without waiting to them to complete or to be
faulted.
- About "Cancel-other-flows": (Let me consolidate what I am
leaning towards so far.) We need to formal define the notion of a
"cancel-other-flows" mechanism. This mechanism is specified and
attached to <flow> construct (and future parallel forEach
construct). When this mechanism is triggered by one of parallel flows,
it will cancel/terminate other parallel flows which are still running, after
the triggering flow is completed. If the other flow activties are scope
activities, the scope will be marked as cancelled.
This mechanism
will be triggered upon the completionCondition is evaluated to be true.
[Note: (a) since this mechanism is triggered by without using any
fault directly. This will again decrease the need of "ignoreFault" construct
(b) This mechanism will affect the wordings of Issue 135 to an
extent.]
[Question: again, should we enforce scope-only activity when
completionCondition is used? I tend to say yes.]
Thank
you all for following and reading this email thread!!!
Regards, Alex Yiu
Trickovic, Ivana
wrote:
Here is a
proposal for the completion condition - it
addresses some issues not tackled so far (at least not in case of
<completeCondition>). It is a version of the proposal for
completion condition for the bundle element. This proposal includes comments
from many people including Frank Leymann, Dieter Koenig, Dieter Roller and Satish Thatte. It does not mean that
they completely support the proposal - they may have issues with any part of
the proposal.
Completion
condition
=====================
The current
semantics of the flow activity is that it waits for all concurrent
activities to complete. The completion also means that an enclosed
activity/scope may end abnormally or be skipped (e.g. the join condition of
the activity evaluated to false). If a fault is thrown within an enclosed
activity/scope and one of the local fault handlers catch the fault (and does
not rethrow the fault), the enclosed activity/scope will be deemed to be
completed (although ended abnormally). If a fault is not caught by any local
fault handler (or is rethrown) the flow activity will terminate all active
concurrent activities and corresponding fault handler may be
initiated.
A
completion condition for the flow activity is needed for scenarios where not
all concurrent activities of a flow activity must complete in order the flow
activity to complete. Note: We are not talking about "successful completion"
of enclosed concurrent activities because that would not be consistent with
the semantics of the current flow activity.
The
completion condition may have different flavors, such
as:
(1) N out
of M
(2) The two
most important requests completed
(3) A
Boolean condition operating upon process
variables
Not all
activities must complete in order the enclosing flow activity to complete.
The way to identify that an enclosed activity did not complete is to
propagate faults. We may distinguish between severe faults and those that
can be ignored. Severe faults cause the enclosing flow activity (or more
precisely, enclosing scope) to terminate the flow activity, including all
active concurrent activities, and corresponding fault handler may be
initiated. Other faults may be ignored - the flow activity is "informed"
that a concurrent activity did not complete but still allows other active
concurrent activities to continue with execution.
Ignore
semantics
=================
Faults
thrown within enclosed concurrent activities/scopes and not handled by local
fault handlers are rethrown. Enclosing <flow> element decides which of
these rethrown faults can be ignored. This new "ignore" semantics should be
part of the completion condition and should apply to all enclosed
activities. This new semantics does not introduce a new fault handling
mechanism. It is needed for identifying how many of the enclosed activities
failed.
Proposed
syntax
================
<flow
standard-attributes>
standard-elements
<completionCondition/>?
<links>?
<link
name="ncname">+
</links>
activity+
</flow>
<completionCondition>
<conditions
branch="xsd:integer"?
expression=xpath?/>?
<ignoreFaults>?
<fault name="qname"?
value="ncname"?/>*
<ignoreAll/>?
</ignoreFaults>
</completionCondition>
Attribute
branch is used to specify a condition
of flavor "wait for N out of M activities to complete", or
more precisely value N. Attribute expression is used to specify a Boolean
condition operating upon process variables or
a condition of flavor "the two most important requests completed".
Both
conditions (branch and expression) may be
specified at the same time. They will be checked when one instance of the
scope activity reaches the end. If at least one condition evaluates to true
all active instances will be terminated.
Element <ignoreFaults> specifies faults that may be ignored. Element fault is used to specify a fault which
may be ignored (fault name and fault data may be specified).
Element <ignoreAll> would mean that all faults thrown/rethrown by any concurrent
activity/scope may be ignored. If this element is
specified <fault> element must be
omitted.
Completion
condition failure
=============================
A new
standard fault, e.g. completionConditionFailure, should be introduced to
notify that the completion condition of a flow activity evaluated to false
(note: all concurrent activities have been completed). The fault is thrown
in the scope enclosing the flow element.
Completion
condition and links
==============================
There
should be no difference between a flow activity with a completion condition
and a flow activity without completion condition. For example, if the
completion condition fails all links leaving the flow activity should have
value "false" (or be reverted to a negative status).
There are
just a few additional rules:
(1) Let's
assume enclosed activity A is the source of a link. If the completion
condition evaluates to true and activity A is not completed it will be
terminated and the value of the link will be set to
"false".
(2) Let's
assume enclosed activity A is the source of a link and the activity failed
but the fault is "ignored" by the enclosing flow activity. The value of the
link will be set to "false".
New
function for completion condition of flavor "the two most important requests
completed"
============================================================================================
For
completion conditions of flavor "the two most important requests completed"
standard attribute "name" must be specified for all enclosing activities in
order to be able to distinguish them. In addition a new function, e.g.
isCompleted('activityName') must be introduced. The semantics of the
function is: if activity completed successfully the function returns value
true.
Example
<completionCondition
expression=
"isCompleted('A') AND
isCompleted('B')"
</completionConditions>
Regards,
Ivana
Hi, Axel,
Thanks for liking parts
of my proposal so far. Actually, the idea of my proposal is borrowed
from a part of presentation from Ivana and Dieter Roller in March F2F. I
just generalize it to apply to <flow> also.
About using
links or not: I don't have a huge opposition to Axel's proposal
which enriches the semantics of joinCondition evaluation. However, it is
not that difficult for people to make mistakes in joinCondition. Look at
the 2-out-of-3 example. The join condition already gets so complicated. If
people make a mistake in their joinCondition, the whole flow can get
struck for no good reasons. (The completeCondition approach will less
likely to have the whole flow struck. Because, it does not introduce a new
parallel activity in the flow). But, if this is what Petri-net-oriented
and control-link-oriented audience really want, I can accept it in a
sense, as long as this is not the only way to achieve similar business
logic.
About static vs dynamic parallelism: Static
parallelism is basically <flow> construct in BPEL. Dynamic
parallelism is "parallel forEach" or "bundle". Issue 4 and 147 are related
issues. http://www.choreology.com/external/WS_BPEL_issues_list.html#Issue4 http://www.choreology.com/external/WS_BPEL_issues_list.html#Issue147
About
"Terminating still running tasks versus proceeding in control flow while
those tasks are still running": I am not 100% that I understand
your question. Let me try to answer it anyway. The completeCondition is
about triggering termination of still-running tasks. The "proceeding in
control flow" (if I understand you correctly) will be handled by an outter
flow. [I guess it is related to (hotel and (train or plane))
flow??]
About changes of "forcedTermination" fault handler:
If you got a chance to see some recent emails on Issue 135, Satish has
recently suggested to remove the notion "forcedTermination" fault and
replace its fault handler with cancelHandler. Frank, Yaron, Edwin and I
all agree with this direction. Because, overloading fault with the concept
of "forcedTermination" is a "false economy" and it creates quite a bit of
unnecessary confusion.
I don't think we should go back to this
route or even further overload the "forcedTermination" fault handler
semantics. Because, fault handling is way too generic and the changes you
mentioned will create even more confusion. (e.g. Will this change of
marking as "completed" apply to all "forcedTermination" fault handler /
cancel handler of all scopes? )
I guess most of us agree that we
should have a new mechanism to kill / do an early completion of a flow in
a nice and clean way. However, overloading a standard fault handler does
not seem to be the best one. I would rather introduce a construct which is
very specific to <flow> or parallel-forEach. For example, this
<completeCondition> construct or maybe a new activity called
<completeFlow />. I will send out more emails on joint
thinking of both Issue 6 and Issue 135. Please stay tuned.
Thanks!
Regards, Alex Yiu
Axel
Martens wrote:
Hi,
I like the simplicity of the syntax of Alex's
proposal. Although, for me it is
quite easy to model in terms of links and join conditions, and I want to keep my proposal alive
(because of the minimal changes
to BPEL's syntax and semantics), it looks like a nice macro to provide more convenience to the
customers.
Alex, could you
shortly explain to me, what do you mean by static and dynamic parallelism? How do you handle the
two different cases after
evaluating the completeCondition: Terminating still running
tasks versus proceeding in control flow
while those tasks are still
running (Sorry, I missed your previous emails)?
I agree to Alex's opinion, that we need a
mechanism to kill parallel flow
nice and clean. This could be done either by a new mechanism which does not throw a fault or by
changing the way fault are
handled. I have discussed already an example of the second case with Ivana, and I like to tell you,
what I have in mind. Look at the
following example:
<scope
name="scopeFlow">
... <flow>
<link
name="linkA"/>
<link name="linkB"/>
<scope name="scopeA"> ... <invoke name="A"
...>
<source linkName="linkA" ...> </invoke>
</scope>
<scope
name="scopeB">
...
<invoke name="B" ...> <source linkName="linkB"
...>
</invoke>
</scope>
<throw name="C" faultName="bpws:forcedCompletion"
joinCondition="linkA OR linkB"
joinEvaluation="immediate"> <target linkName="linkA"
...>
<target linkName="linkB" ...> </throw>
</flow> </scope>
First, I explain the situation: In the example
above, activity C will be
executed if one of the two activities A and B was successfully completed. Activity C throws the
fault "forcedCompletion". Like
each other fault, this forces the scope "scopeFlow" to terminate still running activities. Assume,
there was a fault handler
defined in this scope which catches the fault "forcedCompletion" (omitted in here), the process
continues after scope
"scopeFlow". A problem arises, if scope "scopeFlow" should
be compensated. Because it was exited
from a fault handler, no compensation handler was installed.
Now, I explain my solution: In the example, I
have chosen a new "standard"
fault name: "forcedCompletion". The only necessary change is to allow a fault handler that catches
this fault to install a
compensation handler for the same scope, i.e. to mark the scope "scopeFlow" as "completed" instead of
"exited".
The standard
compensation mechanism will only undo those ("scoped") activities within the scope, which
actually have been successfully
completed, i.e. scopeA or scopeB or may be both.
Axel. ---------------------------------------- Axel
Martens
Post Doc Researcher Component Systems Group IBM TJ
Watson Research Center Hawthorne, NY (USA) Phone: (914)
784-7480 E-mail: amarten@us.ibm.com
Hi,
Here are some examples how to use
completeCondition to express similar business logic in Axel's previous
email:
(A) "Select one out of three" example:
<flow> <completeCondition branch="1"
/> <invoke name="CheckAirlineA" ... />
<invoke name="CheckAirlineB" ... />
<invoke name="CheckAirlineC" ... /> </flow>
(B) "Select two out of three" example:
<flow> <completeCondition branch="2"
/> <invoke name="AskRefereeA" ... />
<invoke name="AskRefereeB" ... /> <invoke
name="AskRefereeC" ... /> </flow>
As you guys can
see, the completeCondition declaration is very straight forward and
simple. No complicated links and joinCondition usage.
(C)
"(Plane or Train) and Hotel" example: It would become two flow
constructed (nested).
<assign>
... <to variable="planeResult"></assign> <!--
initialize "planeResult" with NOT-OK value --> <assign> ...
<to variable="trainResult"></assign> <!--
initialize "trainResult" with NOT-OK value --> <flow
name="checkIterinary"> <flow
name="PlaneOrPlane">
<completeCondition>
fn:planeOK(planeResult) or
fn:trainOK(trainResult)
</completeCondition> <invoke
name="CheckPlane" outputVariable="planeResult" ... />
<invoke name="CheckTrain" outputVariable="trainResult"
... /> </flow>
<invoke name="checkHotel" />
</flow> <switch
name="bookingSwitch"> <case>
<condition>
(fn:planeOK(planeResult) or fn:trainOK(trainResult))
and fn:hotelOK(hotelResult)
</condition> <invoke
name="invokeBooking" ... /> </case>
<otherwise> <invoke
name="writeInformation" ... /> </otherwise>
</switch>
Please
note that regardless whether we pick a link-oriented approach or
completeCondition approach:
- The initialization of result variables are needed
because of potential cancellation of one of the invoke between train
and plane
- The "fn:*()" represents the logic to determine
whether a traveling resource is available. They are used in either the
transitionCondition of links or the condition of switch/case.
As you guys see, we don't need declare to
six links. The completeCondition and case-condition are much simpler and
easier to understand.
I attach a diagram to illustrate the above
flows.
I guess we can still more time in terms of refiniment of
joinCondition evaluation. However, I don't think that should be the only
approach to achieve complete condition related
logic.
Thanks!!!
Regards, Alex
Yiu
Alex Yiu wrote:
Hi,
+1 to what Ivana said in general.
Few
points to add:
- Axel's proposed enhancement to control links
evaluation works to an extent for static parallelism (e.g.
<flow>). However, control links do not work well in dynamic
parallelism (e.g. "parallel forEach" or "bundle"). I think the notion
of completeCondition (borrowed from Ivana and DK) is general enough
and it should be applied to both static and dynamic parallelism. A
general completeCondition mechanism will be easier for BPEL users to
learn.
- Even in a pure static parallelism case,
completeCondition has much better code clarity. It is more declarative
and easier for BPEL users to understand. It will eliminate significant
amount of joinCondition programming, which may be error prone. (I will
send another email later to show how completeCondition can be used to
express the same semantics of Axel's example).
- In one of my previous emails, I also tried to use a
"macro" way to illustrate how outstanding running flows can be
cancelled by throwing a fault within a scope. The illustration has the
same compensation handler installation problem that Ivana has pointed
out. The "illustraction macro" does NOT carry a desirable and intended
semantics. We need to create / describe a new mechanism to cancel
parallel flow without throwing a fault. (That was discussed briefly
between Edwin and me at Oracle).
Thanks!
Regards, Alex
Yiu
Trickovic, Ivana wrote: Axel, I find the
idea interesting. In fact, I was discussing the completion condition
issue with Dieter Koenig during the last f2f meeting and his suggestion
was also to try to resolve this issue using links. And we identified
that several changes need to be done, including removing restriction for
jonCondition, that "the join condition is evaluated as soon as all
incoming links of the activity are determined" - so definitely
"immediate" semantics needs to be introduced. I have the following comments on your
proposal. 1. In your
proposal you are using a fault (bpws:forcedTermination) to terminate all
active parallel branches. But this changes the outcome of the flow
activity. It will always end abnormally and compensation handler (if it
is defined) will never be installed. Although completion condition has
evaluated to true and needed activities have completed the enclosing
flow activity will end abnormally. Is this really intended semantics?
2. Your proposal does not address some pain
points. For example, in case of "N out of M", N<M there many possible
"variations": (A) One
enclosed activity may experience problems but the <flow> activity
may succeed (B) One of
enclosed activities may experience a severe error, which may have impact
on the <flow> activity The question is what to do with running activities? In the latter
case, reasonable behavior would be: if one enclosed activity does not
succeed other running activities should be cancelled and the flow
activity should try to recover. In the former case, we should allow
active parallel activities to complete their work. This is not supported
in your proposal.
Regards,
Ivana
[stuff
deleted]
|