BizTalk Server: Suspend and Resume an Orchestration on Two Way Port Error
Introduction
A few times on the forum, a poster has asked about the combination Orchestrations, Two Way Ports and Retry logic.
In the most recent case, the poster was attempting to build variable retry in the Orchestration and the general consensus was that Port Retry is the better option.
This true, but it’s not the whole story. The Retry options on a Send Port are the best way to configure the number and duration of the retries. It’s built in, well understood and works. But, there’s one very important case that is often left out. That is when the Port eventually does fail.
In most scenarios, the expected pattern Send –> Retries –> Port Exception –> Orchestration Suspends –> Resume. When this happens gracefully, the Admins have time to investigate and correct the underlying problem. The Orchestrations can then be Resumed from a point before the Port error.
The sample code for this article can be found in the MSDN Code Gallery at: BizTalk: Suspend and Resume an Orchestration on Two Way Port Error
The Orchestration Pattern
First off, in the Two Way Port scenario, BizTalk does not offer this pattern out of the box so we have to take a few extra steps to get the desired behavior.
The most important part of this implementation is the loop we manage to retry the entire Port Operation. Since we are prevented from using the RetryTransactionException (the Send and Receive Shape cannot be in the same Atomic Scope), we have re-run the Scope ourselves using Loop Shape.
Message Setup
Before getting to the Port Operation, we have to setup our messages.
In this sample, the Activation Message and Request Message are the same so we assign the received message to the request variable.
Next, we have to assign a temporary value to the Response Message. This is because the Receive Shape is within the Loop and while we know it will always be assigned, the compiler does not so it will throw a unassigned message error.
To avoid the compile error, we assign an XmlDocument instance. If multiple ports use this pattern, the same TempXml can be used.
SuspendOnTwoWayErrorRequest = IncomingMsg;
TempXml.AppendChild(TempXml.CreateElement("t"));
SuspendOnTwoWayErrorResponse = TempXml;
Entering the Loop
The Loop is entered based on the TryCallDb boolean which is initialized to True. When the call succeeds, TryCallDb will be set to False.
TryCallDb
The Port Operation Scope
The Scope containing the Send and Receive shapes is non transactional Scope with one Exception Handler.
The Receive Shape has the Message Property set to SuspendOnTwoWayErrorResponse so when the Response is received the variable is set to the expected message for further processing.
If the Port Completes and the response message is received, we use the Set TryCallDb Expression Shape to set the TryCallDb variable to False to break from the Loop Shape.
TryCallDb = false;
The Exception Handler
When a Port Exception occurs, it is caught buy the Catch Exception box. In this case, we use a generic System.Exception though any combination of Exception Types can be used.
The first action is to log details of the error to the Event Log. Since this process will require Administrative intervention, it is critical to provide as much information and context as possible. The PowerShell script to create the Event Source is in the Project.
System.Diagnostics.EventLog.WriteEntry("SuspendOnTwoWayError", "A call to the database failed. Resume this Orchestration when resolved." + System.Environment.NewLine + System.Environment.NewLine + ex.Message, System.Diagnostics.EventLogEntryType.Error, 100, 0);
Next, we use the Suspend Shape to suspend the Orchestration in a resumable state. The error message configured on the Suspend Shape is identical to the one used for the Event Log.
"A call to the database failed. Resume this Orchestration when resolved." + System.Environment.NewLine + System.Environment.NewLine + ex.Message;
Continue Processing
Once the Two Way Port is successful, hopefully always on the first try, the Orchestration can continue processing normally. In this sample, the only additional processing is sending out the Two Way Port Response Message.
Handling Port Errors
So far, we have created an Orchestration that will gracefully suspend as resumable when a Port error occurs and our business process is covered. However, to create a smooth experience Administratively, there a few side-effects we have to handle.
The most notable is the behavior of the Pipeline Service (the actual Port instance) when the failure occurs.
Enable Failed Message Routing
If Failed Message Routing is not enabled, the Pipeline Service will suspend as non-resumable. While this does not affect the behavior of the Orchestration or the ability to Resume the Orchestration, it does create an admin burden since these Suspended Instances will have to be cleaned up eventually.
Failed Message Handler
To complete the process, the Failed Messages have to be routed somewhere, otherwise a routing error will occur. This sample includes a simple Orchestration who’s sole purpose is to consume failed instances of the SQL Request Message. It consists of just one Receive Port and one Receive Shape.
The Receive Port is configured for Direct Binding and the Receive Shape uses a filter to subscribe to the failed outbound message.
Note: The ESB Exception Portal or other custom solution will also satisfy this requirement.
Running the Sample
App setup
- Deploy the Project from the MSDN Code Gallery: BizTalk: Suspend and Resume an Orchestration on Two Way Port Error
- Run SuspendSampleDb.sql on an existing or new database.
- Import SuspendOnTwoWayError.BindingInfo.xml.
- Modify the SQL and File ports to local URI’s.
A Success Case
Drop SampleRequestMessage.xml into your input folder.
Check the output folder for the output message. The database includes a counter so the message will similar to the following:
<SuspendOnErrorResponse xmlns="http://schemas.microsoft.com/Sql/2008/05/TypedProcedures/dbo">
<StoredProcedureResultSet0>
<StoredProcedureResultSet0 xmlns="http://schemas.microsoft.com/Sql/2008/05/ProceduresResultSets/dbo/SuspendOnError">
<ResultMessage>The Stored Procedure ran. The last Count was: 1</ResultMessage>
</StoredProcedureResultSet0>
</StoredProcedureResultSet0>
<ReturnValue>0</ReturnValue>
</SuspendOnErrorResponse>
A Failure Case
Modify the SQL Uri somehow to make it incorrect. For this sample, I changed the server name.
Once again, drop SampleRequestMessage.xml into your input folder.
Checking Event Viewer, we see the Event logged from the Orchestration.
In BizTalk Administrator, we see the Suspended Orchestration with the custom Error Description.
Resuming the Process
- Correct the error introduced earlier.
- Resume the Suspended Orchestration instance in BizTalk Administrator.
- Check the output folder for the response message. The counter will have incremented from the last run.
<SuspendOnErrorResponse xmlns="http://schemas.microsoft.com/Sql/2008/05/TypedProcedures/dbo"> <StoredProcedureResultSet0> <StoredProcedureResultSet0 xmlns="http://schemas.microsoft.com/Sql/2008/05/ProceduresResultSets/dbo/SuspendOnError"> <ResultMessage>The Stored Procedure ran. The last Count was: 2</ResultMessage> </StoredProcedureResultSet0> </StoredProcedureResultSet0> <ReturnValue>0</ReturnValue></SuspendOnErrorResponse>
Conclusion
While handling Port errors should be a routing practice for all BizTalk apps, building in Resume support and meaningful error logging adds a significant support dimension to our applications. The most beneficial aspect is enabling the Admin teams to diagnose and correct many external causes of application failure.
It would be nice if the out of the box behavior for One and Two Way Ports aligned and the Suspend/Resume pattern was the default, but there is one tremendously significant reason why I don’t think that’s a good idea. Simply, the engine should be presuming that success actually means success. There are many cases where an error is not propagated using a Fault of other Exception type such as when internal app errors are returned in some proprietary and otherwise correct format. In these cases, we have to test and throw our own exception anyway so a pattern like this becomes the only practical option to handle both cases.
Additionally, this pattern offers the ability to add a lot of meaningful context to the error messages such entity identifiers, significant field values. A lot of times, this means a business user can correct the underlying problem themselves.
If a particular Port is used by several Orchestrations, it’s easy to encapsulate this in a Called Orchestration.
See Also
Another important place to find an extensive amount of BizTalk related articles is the TechNet Wiki itself. The best entry point is BizTalk Server Resources on the TechNet Wiki.