Acknowledgments and Negative Acknowledgments (Part 1)

The Biztalk engine has the notion of publishing system level (positive) Acknowledgments (ACK’s) which indicate a successful message transmission and Negative Acknowledgments (NACK’s) which indicate the suspension of a message; these are extremely powerful and can be used for handling the outcomes of asynchronous operations in the engine. For example, consider the scenario whereby an Orchestration transmits a one-way message over HTTP, the Orchestration will publish the message to the Message Box which will route it to the appropriate send port. The transmission of the message is completely decoupled from the Orchestration publishing to the Message Box, so the Orchestration has no notion of whether the transmission actually succeeded or not, instead the Orchestration only knows whether the message was successfully published to o the Message Box. Perhaps the back end web server was down which caused the message to be suspended by the Biztalk engine. The Orchestration would have no way to determine the message was never delivered without some higher level message exchange in the form of a business Acknowledgment. Enter ACK’s and NACK’s.

 

If an Orchestration port is marked with Delivery Notification = Transmitted, and the Send shape in the Orchestration is in a synchronized scope, the Orchestration will wait until it either receives an ACK or a NACK for the message that was transmitted. In the case that the message was successfully transmitted, the engine will publish an ACK ensuring that it is routed back to that Orchestration instance, once the Orchestration receives the ACK it will leave the scope and continue processing. If however the transmission failed and the message was suspended, the engine will publish a NACK which again will be routed back to the Orchestration instance, the Orchestration will throw a DeliveryFailureException which can of course be caught and handled as appropriate in the Orchestration.

ACK’s are published when the Messaging Engine successfully transmits a message over the ‘wire’ and the system context property “AckRequired” is written on the message that was sent and it is set to true. Providing the port in the Orchestration is mark as above the context property is automatically written by the engine so thee is no need to worry about setting it . NACK’s are published when ever the engine suspends a message. Both ACK’s and NACK’s have the following system context properties promoted which can therefore be used in filter expressions for routing:

AckType: set to ACK or NACK

AckID: set to the message ID of the message that this ACK/NACK is for

AckOwnerID: set to the instance ID that this ACK/NACK is for

CorrelationToken: flowed from the message to the ACK/NACK

AckSendPortName: the name of the send port that this message was being sent over

AckOutboundTransportLocation: the outbound url that this message was being sent over

AckReceivePortName: the name of the receive port that the message was received over

AckInboundTransportLocation: the inbound url that the message was received over

In addition all of the message context properties from the message that is being ACK’d / NACK’d are demoted (i.e. if they were previously promoted they will not be promoted on the ACK/NACK) and flowed and from the message to the ACK/NACK. ACK messages do not have any message parts, but of course the message context has a lot of important meta-data. NACK’s on the other hand as well as having all the useful meta-data in the form of context properties, have a message body part the content of which is a SOAP Fault, the format the SOAP Fault can be seen below, it should be noted that the exception message from the exception that the adapter raised is in the SOAP Detail section in the ErrorDescription element:

<SOAP:Envelope xmlns:SOAP="https://schemas.xmlsoap.org/soap/envelope/" SOAP:encodingStyle="https://schemas.xmlsoap.org/soap/encoding/">

      <SOAP:Body>

            <SOAP:Fault>

                  <faultcode>Microsoft BizTalk Server Negative Acknowledgment</faultcode>

                  <faultstring>An error occurred while processing the message, refer to the details section for more information</faultstring>

                  <faultactor>C:\Foo\DeliveryNotification\out\%MessageID%.xml</faultactor>

                  <detail>

                        <ns0:NACK Type="NACK" xmlns:ns0="https://schema.microsoft.com/BizTalk/2003/NACKMessage.xsd">

                        <NAckID>{BD6682EE-1741-4856-8CC7-B2EE36B7874E}</NAckID>

                        <ErrorCode>0xc0c01c10</ErrorCode>

                        <ErrorCategory>0</ErrorCategory>

                        <ErrorDescription>The FILE send adapter cannot open file C:\Foo\DeliveryNotification\out\{505A3211-9081-4720-827B-A0DE2BD124FD}.xml for writing.</ErrorDescription>

                        </ns0:NACK>

                  </detail>

            </SOAP:Fault>

      </SOAP:Body>

</SOAP:Envelope>

 

In the case of an Orchestration port marked as delivery notification required, the DeliveryFailureException that is thrown on a transmission failure is deserialized from the SOAP Fault that is contained within the NACK message body, this is of course transparent to the Orchestration. The Orchestration may get at the exception message string that was thrown by the adapter by casting the DeliveryFailureException to a SoapException and then accessing the InnerXml from the SOAP Detail section, this is shown below:

// Cast the DeliveryFailureException to a SoapException…

System.Web.Services.Protocols.SoapException se = (System.Web.Services.Protocols.SoapException)e.InnerException;

System.Diagnostics.Trace.WriteLine(se.Detail.InnerXml);

Returns the following Xml fragment…

<ns0:NACK Type="NACK" xmlns:ns0="https://schema.microsoft.com/BizTalk/2003/NACKMessage.xsd">

      <NAckID>{BD6682EE-1741-4856-8CC7-B2EE36B7874E}</NAckID>

      <ErrorCode>0xc0c01c10</ErrorCode>

      <ErrorCategory>0</ErrorCategory>

      <ErrorDescription>The FILE send adapter cannot open file C:\Foo\DeliveryNotification\out\{505A3211-9081-4720-827B-A0DE2BD124FD}.xml for writing. </ErrorDescription>

</ns0:NACK>

The publication of ACK’s/NACK’s is a little bit special in that if there are no active subscriptions for them, the ACK/NACK will be discarded. The ACK/NACK is published atomically with the appropriate message, for example, the suspension of a message and the publication of its NACK are in the same transaction within the engine, similarly the publication of an ACK is performed in the same transaction as the deletion of the message from the application queue. Further the engine does not suspend ACK’s/NACK’s.

If the processing of a request-response message exchange pair fails after the receive adapter has successfully submitted the request message and the message is subsequently suspended, a NACK will be routed back to the waiting two-way receive adapter, the receive adapter may then transmit the fault message back to the client. Of course this means that the client would receive a SOAP Fault, what if the client doesn’t understand SOAP Faults? For these scenarios the SOAP Fault maybe mapped changing the format to one that the client is expecting and can handle. Also, once the initial request message is accepted, a processing failure anywhere in the engine resulting in the message being suspended will result in the NACK being routed back to the adapter as its response.

By now you are hopefully starting to appreciate the power of ACK’s and NACK’s, aside from the Orchestration delivery notification and the request-response scenarios above there are many scenarios where they are extremely useful. For example, suppose we have a scenario whereby we use a specific send port to transmit PO’s over a million dollars while all other PO’s are transmitted using a different send port. We could use a filter expression on a send port to route any NACK’s published for messages that are suspended whilst being transmitted on that send port which could subsequently be processed by some backend system.

Alternatively, an Orchestration with a direct binding to the Message Box could be built to receive all NACK’s and subsequently perform some processing to handle the failure based on where the failure happened. Which brings me nicely to Part 2! In Part 2 I’ll describe how such a generic NACK Handler Orchestration may be built which could be used to handle suspended messages, for example moving those messages from the Biztalk applications suspended queue to some out of band application that could handle those failures, along with a sample NACK Handler I'm currently developing, so stay tuned for Part 2 in the near future.