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]
|