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.

Comments

  • Anonymous
    July 04, 2004
    Great post. I'm looking forward to part 2.

  • Anonymous
    July 05, 2004
    Awesome post - opens up the engine in how it handles ack/nacks. Thanks :)

  • Anonymous
    July 06, 2004
    Great info! Looking forward to the next part :)

  • Anonymous
    July 08, 2004
    Good post, but I think I am missing something and could use some help. I have a one way port with property Delivery Notification=Transmitted. I have a scope shape set to sychronized=true and set to long running. On the scope I have an exception block of type Microsoft.XLANGs.BaseTypes.DeliveryFailureException and name of e. Inside the exception block I try to casting the exception to a SoapException within a expression shape (using your sample code snipet) and keep getting a "illegal declarator" declaration may appear only in a service or scope...any ideas? Am I approaching this wrong?

  • Anonymous
    July 08, 2004
    Hmm, try it in user code, that's what I did. When I get time I'll see if I can get it working in an expression shape.

  • Anonymous
    July 21, 2004
    Greetings,

    Spent some time looking at this briefly. I have noticed that I can only set the DeliveryNotification flag if the port binding type is "Specify Later" or "Dynamic". If I choose "Specify Now" for my port binding, I can't set the DeliveryNotification property.
    I understand why I can't do this for "Direct" port bindings, the documentation says so, but what's the story with "Specify Now"? Am I missing something blindingly obvious (which is not beyond the realm of possibilities..), or is this a, erm, "feature", ya, feature, thats the word (cough bug cough)

    cmb..

  • Anonymous
    July 29, 2004
    Hi Kevin,
    I guess there should be way around to handle "could not find matching subscription" error with ACK/NACK. Our scenario is like this.

    Biztalk receives the message from a receive location, some properties are promoted on the custom pipeline, it gets routed to the send port based on promoted properties (subscription filter). There is a possibility, the message might have reached Biztalk with incorrect values, in that case you'll get an error in the event log saying "could not find matching subscription".

    We need to generate a custom error message and handle the following two scenarios

    1. Request-response Receive port. (Need to send the error back syncrhonously)
    2. One way receive port. ( Need to send back error asynchronusly on a different send port)

    I'm working on it at the moment. Any suggestions will be greatly appreciated

  • Anonymous
    July 29, 2004
    The comment has been removed

  • Anonymous
    August 08, 2004
    Ping Back来自:老和山下

  • Anonymous
    March 29, 2005
    When you are invoking WebServices from your BizTalk Server 2004 orchestrations, you might need to ensure that two things happen...

  • Anonymous
    July 14, 2005

    Plenty of folks make the assumption that wrapping a couple BizTalk adapters in an atomic scope makes...

  • Anonymous
    August 22, 2006
    Did anyone else read Kevin Smith&amp;#8217;s blog on ACK/NACK and run out to try it? Well, I sure did and

  • Anonymous
    April 11, 2007
    PingBack from http://www.metadojo.net/metablog/PermaLink,guid,813638b1-89f2-4551-9699-5ec4c72fabef.aspx

  • Anonymous
    January 21, 2009
    PingBack from http://www.keyongtech.com/322565-msmq-adapter-ordered-delivery

  • Anonymous
    May 30, 2009
    PingBack from http://outdoorceilingfansite.info/story.php?id=5082