Tuesday, March 09, 2010                 Register

Keith Pijanowski's Blog
Oct 17

Written by: Keith Pijanowski
10/17/2008 10:13 AM

Understanding the Lifecycle of a Workflow
Contents
 

In my last post I built a simple sequential workflow. I introduced all the details needed to use Visual Studio 2008, a few WF activities, and the workflow runtime to build and run a simple expense report approval workflow.  In this post I want to step back from the mechanics of building workflows and look at the workflow runtime and the lifecycle of workflows when they are managed by the workflow runtime. In other words, I want to investigate how the workflow runtime manages a workflow instance from the time it is first created to the time that it is complete and is no longer needed. Understanding what goes on inside the workflow runtime is important when creating durable and highly available workflow enabled applications. 

You can download the code for this post at the link below.

The WorkflowRuntime class shown in Figure 1 is primarily responsible for managing the lifecycle of workflow instances. The key to understanding how this class manages workflow lifecycles is to understand all of its events and when each one is raised. The WorkflowRuntime class contains 14 events in total. Some of these events are designed to inform the host process about events occurring within the workflow runtime. However, most of these events represent lifecycle changes occurring within each workflow instance being managed.
Figure 1 – Class Diagram of the WorkflowRuntime class
The first three events shown in Figure 1 are known as workflow runtime events. They inform the host about occurrences within the runtime itself. These events are Started, Stopped, and ServicesExceptionNothandled. Figure 2 provides a brief description of these workflow runtime events.     
 
Name
Description
If any workflow service encounters an exception that it cannot entirely handle then it should call the WorkflowRuntimeService.RaiseServicesExceptionNotHandledEvent function which will raise this event and send the exception information to the host.
Indicates that the workflow runtime engine has started. The workflow runtime raises the started event after it has validated core service configuration, started all the services that have been added to it and set the IsStarted flag to true. 
Indicates that the workflow runtime engine has stopped. The workflow runtime raises the stopped event after it has stopped all the services that have been added to it and set the IsStarted flag to false.
Figure 2 – Workflow Runtime Events
 

What is a WF Runtime Service?

A Workflow Foundation Service is a piece of custom code that can be added to the workflow runtime giving it additional capabilities as it manages the execution of workflows.  Examples of WF services that add capabilities are Persistence Services and Tracking Services. WF comes with a Persistence Service and a Tracking service which both use SQL Server as their underlying data store. 

WF Services can also be used to override default behaviors of the WF runtime. An example of a WF service that changes default behavior is the Manual Scheduler service which allows the WF runtime to execute workflows on the current thread as opposed to the thread pool. 

By default these services are not added to the workflow runtime - they must be explicitly added by the developer.

 
It is important to note that the events shown in Figure 2 are not triggered by a running instance of a workflow. These events are designed to notify the host about the state of the runtime itself. The Started and Stopped events fire when the StartRuntime function and the StopRuntime function of the WorkflowRuntime class are called respectively. A workflow service can raise the ServicesExceptionNotHandled event when it encounters an exception that it cannot handle during its execution. 
The other 11 events are known as workflow instance events. Figure 3 provides a brief description of all the workflow instance events. These events will be raised when a workflow instance changes state or when other important events occur while a workflow is in the Running state. It is interesting to note that a running WorkflowRuntime object is itself a state machine that keeps track of running workflows by tracking their status (or state). Figure 4 is a state diagram that shows the various states that a workflow instance may pass through during its lifetime. A workflow instance can be in one of five states:  Created, Running, Suspended, Completed, and Terminated.   It is a common mistake to look at the class diagram of Figure 2 and come to the conclusion that there is a state for each workflow instance event. This is incorrect. There are only 5 states that make up the lifetime of a workflow. 5 of the workflow instance events will be raised when a workflow enters a new state. Specifically, WorkflowCreated, WorkflowStarted (for the Running state), WorkflowSuspended, WorkflowTerminated, and WorkflowCompleted will be raised when an instance enters the Created, Running, Suspended, Terminated, or Completed states respectively. The remaining events inform the host about important changes that occur while the workflow is in the Running state. 
 
Name
Description
WorkflowAborted
Aborting a workflow is only valid when a persistence service is used. When a workflow is aborted the WorkflowRuntime engine throws away the current in memory instance. Application code can abort a workflow by calling WorkflowInstance.Abort. WorkflowInstance.Resume can then be used to restart the workflow from the last persistence point. This should be done only under extreme situations where all work done since the last persistence point needs to be discarded. 
WorkflowCompleted
The workflow runtime engine raises the WorkflowCompleted event after the workflow completes but before it is removed from memory. This event can be used to send output parameters back to the host via the WorkflowCompletedEventArgs class.
WorkflowCreated
The WorkflowCreated event is raised by the workflow runtime engine after a workflow instance has been completely instantiated but before the instance is started via a call to the workflow runtime’s Start function.
WorkflowIdled
A workflow that is not executing any of its activities because it is waiting for an external event, an external message, or a delay activity is said to be idle. The WorkflowIdled event is raised to inform the host of this condition.
WorkflowLoaded
If a persistence service has been added to the workflow runtime engine then the WorkflowLoaded event is raised after the persistence service has restored the workflow instance to memory but before the workflow runtime engine begins to execute any activities.
WorkflowPersisted
If a persistence service has been added to the workflow runtime engine then the WorkflowPeristed event is raised after the state of the workflow instance has been saved by the persistence service.
WorkflowResumed
A workflow instance that has been previously suspended may be resumed and begin executing again at the point it was suspended. The WorkflowResumed event occurs after the workflow instance has been scheduled to begin executing but before any activities begin to execute.
WorkflowStarted
The WorkflowStarted event indicates the entry of the workflow instance into the Running state.    The WorkflowStarted event is raised when the workflow instance’s root activity is scheduled for execution.
WorkflowSuspended
The WorkflowSuspend event occurs when a workflow instance is suspended and enters the Suspended state. A workflow can be suspended by the host via a call to the WorkflowRuntime.Suspend function, by a Suspend activity explicitly placed within the workflow definition, or implicitly by the workflow runtime itself. A suspended workflow can be resumed at the point it was suspended. 
WorkflowTerminated
The WorkflowTerminated event occurs when a workflow instance enters the Terminated state. A terminated workflow is cleared from memory. If a persistence service is used then the persistence service is notified that the instance has been terminated. The SqlWorkflowPersistenceService will delete all state information for terminated workflows. A workflow can be terminated by the host via a call to the WorkflowInstance.Terminate function, by a Terminate activity explicitly placed within the workflow definition, or by the workflow runtime engine when an unhandled exception occurs. The terminate event is raised after a workflow is terminated but before the instance is removed from memory.
WorkflowUnloaded
If a persistence service has been added to the workflow runtime engine then the WorkflowUnloaded event is raised after the persistence service has successfully saved a workflow instance’s state but before the instance is removed from memory.

Figure 3 – Workflow Instance Events

 

Figure 4 – Workflow Runtime States and Events
 
The host process can be made aware of the occurrence of these events by registering delegates with the workflow runtime. Figure 5 shows a revised version of the Main function from the ConsoleHost project created in my last post. In this code a delegate has been registered for each of the workflow runtime events and for each of the workflow instance events. Figure 6 is the implementation of the delegate for the workflow idled event. Notice that for workflow instance events the delegate gets passed the workflow instance ID of the workflow instance that raised the event. In the code download for this post all the other events have been setup in a similar fashion.
static void Main(string[] args)
{
    // Create a new Workflow Runtime
    WorkflowRuntime wr = new WorkflowRuntime();
 
    // Workflow Runtime events
    wr.ServicesExceptionNotHandled += OnServicesExceptionNotHandled;
    wr.Started += OnStarted;
    wr.Stopped += OnStopped;
 
    // Workflow Instance Events
    wr.WorkflowAborted += OnWorkflowAborted;
    wr.WorkflowCreated += OnWorkflowCreated;
    wr.WorkflowCompleted += OnWorkflowCompletion;
    wr.WorkflowIdled += OnWorkflowIdled;
    wr.WorkflowLoaded += OnWorkflowLoaded;
    wr.WorkflowPersisted += OnWorkflowPersisted;
    wr.WorkflowResumed += OnWorkflowResumed;
    wr.WorkflowStarted += OnWorkflowStarted;
    wr.WorkflowSuspended += OnWorkflowSuspended;
    wr.WorkflowTerminated += OnWorkflowTerminated;
    wr.WorkflowUnloaded += OnWorkflowUnloaded;
 
    // create and setup the workflow parameter
    ExpenseReport expense = new ExpenseReport();
    expense.Amount = 2500;
    expense.Employee = "Keith Pijanowski";
    expense.Title = "Platform Strategy Advisor";
 
    // create the dictionary object to hold the parameter
    Dictionary<string, object> parameters = new Dictionary<string, object>();
    // this is a key/value pair
    parameters.Add("Expense", expense);
 
    // pass the type of the workflow to be created and any parameters
    WorkflowInstance instance =wr.CreateWorkflow(
        typeof(SequentialExpenseReportApproval),
        parameters);
 
    instance.Start();
 
    Console.ReadLine();
}
Figure 5 – Runtime Events and Instance events
 
static void OnWorkflowIdled(object sender, WorkflowEventArgs e)
{
    Console.WriteLine("Workflow Idled. Instance ID: " + e.WorkflowInstance.InstanceId.ToString());
}
Figure 6 – Sample Delegate
 

When the sequential workflow from my previous post is instrumented as shown in Figure 5 and Figure 6 then the output shown in Figure 7 is produced. Notice that we are notified when the workflow instance is created, started and completed. Also notice that this workflow does not need to idle in order to wait for external events, external messages or a delay activity. Consequently there is no need to unload and persist this workflow.

 

Figure 7 – Output from the Expense Report workflow

In this section I want to simulate a long running workflow and observe the workflow instance events as the workflow executes. To simulate a long running workflow I will add a Delay activity to the expense report approval workflow which I created in my last post. Using the Delay activity is easy. Merely drop it onto the sequential workflow designer in the desired location. Figure 8 shows the expense report workflow after a Delay activity has been added to it. I want the delay activity to always execute so I did not place it inside one of the IfElseBranch activities. The Delay activity can be configured using the properties dialog as shown in Figure 9. Here I have named the activity delayTest and I have configured it to delay execution of the workflow for 1 second.

 

Figure 8 - Adding a Delay Activity

 

Figure 9 - Properties for the Delay Activity
 
When the modified workflow is run the output shown in Figure 10 is produced. Notice that the Idled event is raised indicating the workflow is no longer running but waiting for the Delay activity’s timeout duration to expire. We would have observed the same behavior if instead of a delay activity we had placed a web service Receive activity or a HandleExternalEvent activity. With respect to lifecycle behavior all these activities result in the same thing. That is the workflow going idle and waiting. I used a Delay activity here for simplicity.

Investigating a timeline that shows the lifecycle of a workflow from creation to completion will clarify the relationship of a workflow’s state and the workflow instance events that are raised by the workflow runtime. 

 

Figure 10 - Output messages showing an Idled event

Figure 11 shows a timeline of a long running workflow from the time it is created until the time that it completes. This timeline is of a workflow that is never aborted, suspended, terminated or persisted. Once the workflow reaches a point where an external event is needed it will raise the Idled event indicating that it is no longer executing. By default the workflow runtime will keep all workflows that are waiting for external events, external messages, or delay activities in memory. Not only does this waste system resources but it also leaves the workflow in a vulnerable situation. If the system or the process in which the workflow runtime is located crashes then the data that the workflow instance contains and the work that it has done are lost. 

 

 

The workflow instance has been created via a call to CreateWorkflow(); but, the workflow has not been started by calling StartWorkflow().
The workflow instance has just entered the Running state.

This part of the timeline represents a loop. For a long running workflow one Idled event will be raised by the runtime every time the workflow pauses to wait for an event, message, or a Delay activity. 

Note: This diagram shows the events in the order that they are raised to the host. The Idled event always occurs before the Persisted and Unloaded events.

The Completed event is raised just prior to the workflow instance being removed from memory. This gives the workflow a chance to pass output parameters back to the host if needed.
 

 

 

 

 

 

 

 

 

 

Figure 11 – Timeline for an in-memory lifecycle

 

Clearly what is needed to solve the problem described in the last section is a way to persist a workflow once it has gone idle.  Once a workflow is persisted the workflow can be unloaded from memory. 
Fortunately, the Workflow Foundation provides a mechanism that allows workflow instances to be persisted and unloaded when they are not executing. Persisted workflows can be loaded when they need to execute again. This is done by adding a persistence service to the workflow runtime. A persistence service is responsible for saving, and retrieving workflow instance state from some form of non-volatile storage. The runtime can still listen for messages on behalf of the persisted workflow. When a message arrives for a workflow that has been persisted the workflow runtime can then load the workflow into memory and begin executing activities at the point in which the workflow went idle. 
Figure 12 is a revised timeline of the long running workflow shown in Figure 11. This timeline shows the workflow being persisted, unloaded and loaded. It also shows some additional workflow instance events that the workflow runtime will raise in order to inform the host when a workflow has been persisted, unloaded and loaded. What is compelling about the timeline shown in Figure 12 is that only workflows with executing activities are in memory. All other workflows are persisted and unloaded. 
 

 

 

 
The workflow instance has been created via a call to CreateWorkflow(); but, the workflow has not been started by calling StartWorkflow().
The workflow instance has been started via a call to StartWorkflow(). At this point the workflow is in the Running state.

This part of the timeline represents a loop. For a long running workflow one Idled event will be raised by the runtime every time the workflow pauses to wait for an event, message, or a Delay activity. 

Note: This diagram shows the events in the order that they are raised to the host. The Idled event always occurs before the Persisted and Unloaded events.

The persistence service is called to remove the workflow instance from the persistence store. Then the Completed event is raised before the workflow instance is removed from memory. This gives the workflow a chance to pass output parameters back to the host if needed.
 

Figure 12 – Timeline for a workflow lifecycle when a persistence service is used

 
The workflow runtime determines when a persistence service is called. Workflows are persisted at locations known as persistence points. Below is a list of persistence points that will cause the workflow runtime to call a persistence service if one has been added to the workflow runtime.
·         When a workflow goes idle.
·         Before a workflow instance completes. (This removes any instance data from the underlying persistence store.)
·         Before a workflow instance terminates. (This removes any instance data from the underlying persistence store.)
·         On the completion of activities that are marked with the PersistOnCloseAttribute attribute.  
·         When a developer calls WorkflowInstance.Unload or WorkflowInstance.TryUnload.
 
The first three persistence points provide default durability for all workflows executing within a workflow runtime that has been equipped with a persistence service. The last two persistence points provide added flexibility for developers that would like to add additional persistence points to their workflows. 
All of the out of the box WF activities that utilize transactions use the PersistOnCloseAttribute attribute. Additionally, all custom activities that use transactions should also use this attribute. Finally, any other custom activity may use this attribute if the design of the activity dictates that the state of the workflow instance should be persisted once the activity is done executing. 
Workflow Foundation provides the classes and the database scripts that allow Sql Server 2000, Sql Server 2005, Sql Express, and MSDE to be used as a persistence store. However it is important to note that Workflow Foundation also provides the WorkflowPersistence base class and the IPendingWork interface which can be used by developers to create custom persistence services that use any storage mechanism. Therefore it is possible to use Oracle, DB2 or MySQL for persistence. 
In this post I introduced the workflow runtime and its default “in-memory” lifecycle. I also presented a lifecycle that saves and unloads idle workflows. This is the lifecycle used when a persistence service is added to the workflow runtime. Persistence Services allow the workflow runtime to use system resources more efficiently when the workflows it is managing are long running. Persistence services also provide a more durable environment for workflows to run.

In my next post I will show how to setup a Sql Server database that can be used by the Sql Server Persistence service. I will also show how to add the Sql Persistence service to the workflow runtime. Finally I will show the various configuration options of this service.

Technorati Tags:  ,
Bookmark:   Digg    Del.icio.us    Reddit

 

Tags:

6 comments so far...

Re: Understanding the Lifecycle of a Workflow

This is applicable to processing paperwork in maybe a financial or insurance industry as I understand it. What interests me is maybe how I can apply this concept to Automated Test Equipment. I am wriring an application at the moment using C# in VS 2008 that is a form application that guides a test operator in testing PC boards on the assembly line floor. The application runs on a networked PC running XP. The PC is connected to two test fixtures with serial ports. One fixture is used as the stimulus while the other fixture contains the PCB Device Under Test that is interigated by serial commands through the other port. The test application is initiated to run by scaning the barcode on the PCB DUT. The test results are recorded in a file that is created using the barcode serial number and then is tracked in the system as the pcb is then inserted into the product and so on .....

By Jiri Orlt on   10/29/2008 10:49 AM

Re: Understanding the Lifecycle of a Workflow

The examples I have been using in my WF series are "Electronic Forms" based examples. In other words workflows that are initiated by a human submitting some information via an electronic form. However, there are a great many uses for WF. You can use WF for system to system workflows and you can use it for lower level functions of an application.

In your example WF could be used to manage the file that contains the test results from your PCB device. WF would execute within the Windows forms application - it does not need to be in a server environment. All this being said the question then becomes is WF too big a tool for your problem. If you are only creating a file and saving it in some location then using WF is overkill. If there is a lot of logic that gets executed before the file is created and subsequent to its creation then consider WF. Especially if it is desired to have this logic in a visual form and if this logic changes frequently. WF is also well suited for managing information that is being tranmitted from an application and received by an application. So if your application has a lot of system to system communications then WF could help.

If you need help getting started send your design documents to my Microsoft email - keithpij@microsoft.com - I can show you the activities to consider and the workflow type that would be best for your problem.

I hope that helps,

-Keith

By keithpij on   10/31/2008 5:15 AM

Re: Understanding the Lifecycle of a Workflow

Hi Keith
Thanks you for a series of great articles on WF . I am new to WF and to .net (have been using Delphi for many years) but find your articles extremely useful.

Just wondering if you can point me to the right direction in using WF with ASP.Net

Thanks for your assistance

Andrew

By statcom on   6/23/2009 4:05 AM

Re: Understanding the Lifecycle of a Workflow

I would start with this article to understand using WF in an ASP.NET web application: http://msdn.microsoft.com/en-us/magazine/cc163623.aspx

I would also check out http://msdn.microsoft.com/en-us/magazine/cc164251.aspx to learn about the new "Send" and "Receive" activities that were introduced in .NET 3.5. These activities provide better integration between WF and WCF.

I hope this helps,

-Keith

By keithpij on   6/25/2009 4:57 AM

Re: Understanding the Lifecycle of a Workflow

Hi Keith,

Excellent articles and you explain so perfectly with a lot of images and good explanation. I'm totally new to WWF. May I ask a couple of fundamental questions:

A) What is the "real" purposes of delay activities? Could you provide me a real-life example as where you would use them?
B) In your next article, you talked about the SQL persistence...In that you mentioned that once you made ExpenseReport class into Serializable, that would also get serialized. So my question is that when the unload event occurs, it not only stores the workflow instance details but also the ExpenseReport object details? Where does it get stored in the table? Is it all encapsulated from our view?

Thanks again, JK

By JK on   7/23/2009 10:28 PM

Re: Understanding the Lifecycle of a Workflow

Hi JK,

A) The Delay activity is often used as a non-elegant way to solve real world coordination problems when you can communicate with another system but that system cannot communicate with you. For example, if you sent something to a legacy system and you needed to wait for it to complete its work you could send your message - delay for a period of time - check to see i the work is done - delay again if necessary, etc.

B) You are correct - if you have a workflow property that is built from a class marked as Serializable then that property is serialized along with the workflow instance. The information is somewhere in the Sql Persistence database. I am not sure of the exact table. For the most part it is hidden from the developer. If you need to get information about the status of a workflow the tracking service may be a better option.

I hope this helps,

Keith

By keithpij on   7/28/2009 5:15 AM

Your name:
Title:
Comment:
Add Comment    Cancel  
The Workflow Foundation Series
The SOA Series
Other Posts
Privacy Statement    |    Terms Of Use Copyright 2007 by Keith Pijanowski