Choose end event types wisely!
The process composer palette offers as many as three types of end events, conclusively named End, Termination, and Error End. We sometimes observe uncertainties in which one to choose for a particular scenario.
While the three types of end events all end a control flow branch, they substantially differ in their behavior. As a result, the modeled flow may behave very differently. Roughly spoken:
- (Regular) end events complete a single control flow branch and, thus, consume a single “token” from their inbound edges. This type of end event will not consume tokens from other process branches such that a process instance may not be terminated from a single firing of an end event.
- On the contrary, terminating end events immediately complete the entire process instance, including all task and subflow instances. All existing tokens are consumed and no new activities are started.
- Error end events are a means to explicitly raise an exception from within a subflow. An error end event will immediately cancel the entire process instance in the same way as a terminating end event. Additionally, it throws an exception event which can be caught using boundary event handlers in some outer scope.
At a first glance, differences between regular and terminating end events may appear to be subtle at best. However, there are clearly situations where it does make a tremendous difference which one to choose. In fact, you may turn a correctly functioning flow into a misbehaving contraption, just by making the wrong choice here. For an illustration, let’s have a look onto an example flow from my last posting:
As we have discussed before, this flow will ultimately run into a deadlock if the boundary event attached to “Activity 2” is triggered. Apart from encapsulating everything in between the AND split/join gateways into a separate subflow (as suggested in my previous posting), there is another, less costly solution to break out of the deadlock.
If the regular end event is simply replaced by a terminating end, the flow will be completed despite of “dangling” upstream tokens. This is due to the fact that a terminating end immediately stops the flow and, subsequently, cleans up any remaining tokens within the completed process instance.
Then again, there are situations where terminating end events may lead to a non-deterministic behavior. Have a look onto the flow below which constitutes a slight deviation from the afore-discussed example.
In fact, all we have done is to remove the AND join gateway in front of the end event. Arguably, there is no need to perform synchronization as there are no downstream activities before the end event. And in fact, it’s this AND join gateway which is the root cause of the afore-discussed deadlock issue. This is why you may be tempted to drop it to get rid of this issue once and for all. But here’s the catch: there is no way of knowing whether or not both “Activity 2” and “Activity 3” have completed when the terminating end event is triggered. This is due to the fact that the process server Netweaver BPM runs most process artifacts (including activities, gateways, events) asynchronously and in parallel. For instance “Activity 2” may have completed, thus enabling the end event but “Activity 3” has not yet started. The process server may decide to next run either “Activity 3” or “Terminating End”. Obviously, that makes a big difference as – when completed – this process instance may have run “Activity 3” or not. A simple, but effective counter-measure to this non-determinism is depicted in the diagram below:
As regular end events solely consume a single token but initially leave the process instance unaffected, remaining tokens may happily continue in their course of executing downstream artifacts. Only when the last token has disappeared, the process instance ultimately becomes a “goner” and is silently completed.
Error end events behave like terminating end events to the extent that the affected process instance immediately ends. However, the process log will record this instance as “cancelled”, i.e. abnormally completed. If the process instance costitutes a subflow of a parent process, no token is passed to the outbound edge of the parent process. Instead, a boundary event may optionally be attached to the parent process’ subflow activity to “catch” the error and pass spawn off an exception handler control flow branch.
Recommendation: Regular and terminating end events offer great ways to reduce model complexity. As a rule of thumb: use regular end events whenever your process does not entail deadlocks. Terminating end events are a great choice if you know for certain that at the time a token hits the end event, any upstream activity that was supposed to run has completed by then. Error end events should exclusively be used to stop processes instances which have run into a semantical error from continuing.
To some extent, choosing the right end event even saves you quite a bit modeling time since there are fewer artifacts to to add, configure, and wire up in your process diagram. And besides, it may even make the resulting process instances execute faster and spend fewer resources at runtime. But most importantly, when end events are chosen right you can circumvent many modeling issues. So it’s definitely worth spending some thoughts on this subject.
In the next postings, we will have a look on “exit strategies” for loops and recursive subflow invocations and will also shed some light on how referenced subflows behave and complete. Stay tuned!