- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
Workflows tend to be long running processes. They typically "live" for anywhere from several hours to several days. Very little of that lifetime is spent doing actual computation. Most of a workflow's duration is idle time. To avoid tying up system resources while the workflow is sitting around doing nothing, the workflow engine has to yield its processing thread to other system processes, and only wake up when it has work to do. This is accomplished using wait states and events.
The term "event" may be familiar, but "wait state" probably isn't. A "wait state" is just another way of saying that an activity or workflow doesn't have anymore work to do. Both activities and workflows use wait states. When all activities on all branches of a flow are in a wait state, then the entire workflow can go into a wait state, and the system can use its processing thread to do other work. "Events" are the signals that wake the workflow back up and tell it to get back to work. The easiest way to understand how this works is to see it in action, so let's work through some examples.
Example 1: A really simple waiting flow
Our first flow uses 2 activities, Log Message, and Wait For WF Event.
Log Message is not a waiting activity. When the flow transitions into the activity, it writes a message to the context log, then transitions out. It never waits. Branch, If, Switch, and Set Values are some other non-waiting activities.
Wait For WF Event is a waiting activity. When the flow transitions into a waiting activity, it does some work, then goes into a wait state. Every waiting activity is waiting for a specific event to wake it back up. When that event is received, the activity has an opportunity to do some more work, and potentially finish and transition out (or it can go back to waiting). In the case of Wait For WF Event, there is little-to-no "work" being done either before the wait state or after the event. When the flow transitions into a Wait For WF Event, the activity simply registers the event its going to wait for. In this example, that's "myEvent".
When the Wait for WF Event activity receives that event, it just finishes and transitions out. However, other "waiting" activities, like Approvals, Tasks, and Orchestration activities, may do signficant amounts of work before and/or after the wait/event cycle. Some of them also allow you to write scripts to execute custom logic (ie. Sensor scripts on Orchestration activities, or Advanced Approval scripts on the approval activities).
When I run my simple example flow, I get an executing context that looks like this.
Here you can see that the Log Message activity ran and finished, and the Wait For activity is waiting, just what you would expect. To get this flow to complete I need to fire the "myEvent" event. When I do that (and we'll discuss how to do that later in the article), the activity receives the event, completes, and the workflow starts to transition again, in this case to the End activity, and finishes.
Example 2: Multiple Branches
Now that we've seen a waiting activity in action, let's add a second branch to the flow and see how waits affect multiple branches.
Here's the modified workflow:
The two branches do the same thing, log a message and wait for an event. Note that the two waiting activities are waiting for 2 different events: "eventA" and "eventB". When I run this flow and look at the context diagram, I can see that, as you probably expected, the 2 Logs have finished and the 2 Wait For activities are waiting.
What's interesting here is that our 2 branches are executing "in parallel". I air-quoted "in parallel" for a reason, and we'll talk about why in just a second.
First, I'm going to fire "eventA". Now my context diagram looks like this:
Whoa! The second Wait For Event activity cancelled, and the workflow completed. You might have expected the flow to wait on both events, but it didn't. To understand why, we need to look at how the workflow engine processes activities and transitions.
The Workflow Execution Queue
The Workflow Engine operates like a queue. Activities are pulled off of the top of the queue and executed. When an activity finishes, its transitions are evaluated, and, for every transition that evaluates to "true", the activity on the other end of the transition line is added to the bottom of the queue. Here's a representation of this execution queue as we step through the Example 2 flow.
In Step 1, we're seeing the queue as it looks as the Begin activity finishes. Begin transitions to the two Log activities, so the engine queues those 2 activities to execute. Maybe you can see why I air-quoted the "in-parallel" statement about the two log activities? The activities aren't really running at the same time, on separate threads. They actually run sequentially, but both will be run before any additional activities will run. So branches are transitioned in parallel, but all workflow processing is single-threaded and sequential. That's what we're seeing in steps 2 and 3. Step 2 represents the queue's state at the end of the Log A activity, Log B is now at the top the queue, and Log A's transition to Wait for eventA has added that activity to the bottom of the queue. Step 3 is the finish of the Log B activity, Wait For eventA is at the top of the queue, and Wait for eventB has been added to the bottom.
Make sense so far? Step 4 & 5 is where the plot thickens. In Step 4, both of our Wait For Event activities have executed, both are waiting for their respective events. Neither finished, so neither put any new activities into the execution queue. The queue is currently empty. This is a wait state for the entire workflow. The workflow engine will release the execution thread and return control to other parts of the system. This flow is now at rest and will not wake up until it receives an event. Step 5 shows why the second Wait For Event activity gets cancelled. We've received "eventA", so the Wait for eventA activity is able to complete. That activity transitions to End, which is added to the execution queue. End will be the next activity to execute. The End activity's job is to cancel any non-finished activities in the flow. In our case, that means Wait For eventB gets cancelled, and the flow completes.
Example 3 - making both branches from Example 2 execute
Example 2 was enlightening (I hope), but misses the mark if you want both branches to execute all the way through. Its easy enough to fix it up by using the Join activity.
Join is another waiting activity. Basically, Join is waiting for all of its incoming transitions to be processed. When you run this flow and get to Step 5 (firing eventA), the flow will transition into the Join activity. Join will realize that it has another active transition that hasn't completed (from branch B) and go into a wait state. Whenever an activity completes, the engine uses an event to wake up Join and have it re-evaluate its input transitions. When they are all complete, the Join activity will complete and transition to End.
Complete vs Incomplete joins
Notice that the Join activity includes 2 outgoing transitions, labeled Complete and Incomplete. This is a frequent source of confusion for workflow authors. Example 3 was an example of a Complete join. The Join waited until all of the activities that transition into it finished. But what's an Incomplete join? Here's how you might get one:
I've added an If activity right after the Begin that chooses to execute either Branch A or Branch B, but never both. That means the Join activity will never get both of its incoming transitions to complete. Based on what I've told you about Join up to this point, it would be stuck waiting for all eternity on a transition that will never, ever complete. But luckily, Join is smart enough to recognize this situation, and will use its Incomplete transition to escape. An incomplete Join means that all branches that can complete have, but there were other branches that cannot complete. Its a subtle difference. In 95% of real-world workflows I've seen, the Complete and Incomplete transitions are both going to the same activity. But if you have a use case that requires differentiating between them, you can.
Firing Events
I've used the phrase "fire the event" a lot above, but so far given no clues about how to do that. Well, the dirty little secret is that you have have to use script to fire a workflow event today. There's a couple methods in the Workflow script include for doing it (search for fireEvent and/or broadcastEvent), and they are pretty well documented, so I'm not going to go into great detail on how to use them. You can use them from UI actions, business rules, workflow activities, etc. Basically anywhere you can write server-side script. For this article, I slapped together a UI action on the wf_context table called Broadcast MyEvent. Every time I wrote "fire the event" in the text above, I was clicking that UI Action's button. Here's the total script from the action:
new Workflow().broadcastEvent(current.getUniqueValue(), 'myEvent');
What about the Create Event Activity?
If you've poked around the activity tree a bit, you may have come across the Create Event activity. So how does that activity come into play in these workflow events we've been exploring?
The short answer is, it doesn't. Create Event creates system events, not workflow events. System events and workflow events are completely unrelated to each other. System events can trigger processing outside of workflow and are configured via the System Policy...Events module. The Create Event activity enables a workflow to fire one of these system events. HOWEVER, system events don't wake up or cause a workflow to progress. Caveat: some system events trigger scripts which use fireEvent/broadcastEvent to send a workflow event back to a workflow.
The End
This post looked at a lot of stuff that's happening under the covers when a workflow executes, possibly more than you ever wanted to know. Hopefully, understanding the concepts of events, wait states and the execution queue will help you to author better workflows and make debugging easier when they aren't behaving the way you expected.
- 3,137 Views
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.