Since I am coming from the BizTalk world, I feel very comfortable in implementing Workflows, using the Microsoft Workflow 4.0 runtime. The engine comes with a lot of similar activities and capabilities as the XLANG engine provides us for more than 6 years already.
One of the typical patterns that involve long-running processes is correlation and more in specific; convoys. Each time different messages or calls have to be related together in order to achieve something, we are using convoys. Convoy processing is a typical design pattern.
There are two types of convoys, which I will demonstrate both:
- Sequential convoy – Items that are in a order, one after another (often messages of the same type)
- Parallel convoy – Items that are received in any order, but that all need to be received, before processing continues
The main difference in the two types of convoys is the order of receipt of the items.
This blog post shows two scenarios in an online travel website, where users can rate hotels and where hotels can be added, by consuming workflow services from any mash-up application.
The source code of my example can be found here.
Correlation
The concept of correlation is when various messages or calls need to be linked to the same instance of a long-running workflow. These calls are typically related, based on content in the message, or based on context of the call (endpoint, for example).
An example would be where a workflow creates a user task on sharepoint and waits until the task is completed. Multiple instances of that workflow can run in the same time and tasks can be completed in a different order than they were created. Therefore, we need to make sure that the task completion event is sent to the correct instance of the workflow that created the task. This is called correlation.
Setting up correlation in workflow is done by defining a variable of type System.ServiceModel.Activities.CorrelationHandle This variable needs to be scoped to make sure it is available for all receive and send activities that need to participate in the correlation.
Correlations are initialized or followed on the messaging activities.
Sequential convoy
The first scenario is a workflow service that processes the user rates that is given for a certain hotel. A requirement for the web site is that the rating for a hotel should only be published when at least 5 user ratings have been received for that specific hotel. This is needed to prevent influences of one specific rating.
This is why we implement a sequential convoy.
Receive web service operation and correlation
For this, we have a sequential workflow that exposes a web operation: SubmitHotelReview. This operation accepts a message of type HotelReview (a datacontract class with some basic parameters). All calls to this workflow service will be correlated to the same instance, based on the hotel ID.
The most important settings of the Receive activity are shown in the following table.
Correlations | |
CorelatesOn | Xpath query key 1 : sm:body()/xg0:HotelReview/xg0:HotelId |
CorrelatesWith | hotelCorrelationHandle |
CorrelationInitializers | no specific changes here (only the request/response handle is initialized here) |
Misc | |
OperationName | SubmitHotelReview |
ServiceContractName | {http://blog.codit.eu/workflow/convoys/}IHotelReviewService |
CanCreateInstance | Checked (True) |
A common mistake is to also specify an entry in the CorrelationInitializers property to initialize the content-based correlation handle (hotel id). But doing this, would result in the following exception: An instance key of value {Guid}’ already exists. This could be because there are multiple MessageQuerySets defined that evaluate to the same CorrelationKey. The reason for this is that the correlationkey would be initialized twice, which results in this exception.
Looping condition for sequential convoy
To have the sequential convoy implemented, we need to correlate all receives together and therefore we can add a loop, using the DoWhile activity. Here we just add the receive activity in the ‘Do-body’ and define a condition, making sure we continue processing (publishing the hotel rates), once five reviews have been received.
The entire workflow service is displayed in the following screenshot. (click to enlarge)
Parallel convoy
The second scenario is a workflow service that adds new hotel information to the web site. Data for these hotels is coming from two different sources. One consumer of our workflow service will pas in the general hotel information, where another consumer application will provide pricing information for a hotel. The sequence of these events is unknown and can differ from one hotel to the other.
This is why we implement a parallel convoy.
Receive web service operations and correlation
For this, we have a sequential workflow that exposes two web operations: SubmitGeneralInformation and SubmitPricingInformation. Both receive activities correlate on the same correlation handle that is correlating on the HotelId.
The settings of the receive activities are similar to the settings listed in the sequential convoy sample.
Parallel activity for multiple receives
To make sure we can accept both pricing and general information, we add the receives in a parallel shape, which will make sure we will only continue the flow if all branches have been successfully completed (and thus all messages have been received).
The entire workflow service is displayed in the following screenshot. (click to enlarge)
Conclusion
Implementing convoys in Workflow 4.0 is rather straightforward and does not require very complex tricks or configuration. The magic is all in the correlation handling.
In comparison with BizTalk, I really like the sequential convoy implementation, since we now only have the need for one single Receive activity, where in BizTalk the initializing and the following receives need to be two different shapes.
Something I curious about is if there would also be the concept of zombie messages that are typical in the convoy scenarios. (when a message is being received at the exact same time as the looping condition of the sequential convoy completes.)
Sam Vanhoutte, CODit