Zombie messages are messages that were routed to a running orchestration from the messageBox and were currently “in flight” when the orchestration instance ended. In other words, we have not consumed all the incoming messages in this orchestration instance.
The most common causes are as follows:
- Terminate control messages: when a control message exists in an orchestration that cancels the job currently running, zombie messages can be created as a result;
- Parallel listen receives: an orchestration instance is terminated at the same time as new messages arrive. This situation has been discussed (AGGREGATE MESSAGES IN AN ORCHESTRATION WITH A SEND PIPELINE)
For more information, take a look at https://docs.microsoft.com/fr-fr/biztalk/core/zombies-in-biztalk-server.
When zombie messages are created, this type of message appears in the BizTalk console:
In such situations, we need to deal with the zombie messages. In this article, we are going to use Parallel Listen Receive as our example to describe the problem and the cause. We will also suggest two solutions to resolve the problem.
Description of a simple case
Let’s suppose that the messages we are going to send in an orchestration are invoices, structured as follows:
The “Year” field is promoted, and the property schema is as below:
The objective is to be able to correlate on this “Year” field in the orchestration.
An overview of this correlation is shown below:
For the first receive shape, called ReceiveInvoice, a Correlation Set named CorProperty is to be initialized, with its Correlation Type being the promoted property, i.e. Year (see below).
A targetTimevariable is created, of type System.DateTime, for the WaitDelay Expression shape. This will be used as a timer:
targetTime = System.DateTime.Now.AddSeconds(120);
In our example, we will run only simple processing. In the AssignMessage shape:
msgInvoice = msgInvoiceIn;
msgInvoice(*) = msgInvoiceIn(*);
RefInvoice = xpath(msgInvoice, "string(/*[local-name()='Invoice']/*[local-name()='Reference'])");
Year = xpath(msgInvoice, "string(/*[local-name()='Invoice']/*[local-name()='Year'])");
RefInvoice = "Invoice_" + Year + "_" + RefInvoice;
xpath(msgInvoice, "/*[local-name()='Invoice']/*[local-name()='Reference']") = RefInvoice;
Then send the message processed using the SendPort.
We create a Boolean variable named vExistNextInvoice, which we set to True, as the condition for exiting the loop. In the ManageNextInvoiceloop and the left branch of the Listen shape, we will receive the next invoices, process then in the same way, and send them to the SendPort. At the same time, in the right branch, we run a timer in the shape:
When the timer ends, vExistNextInvoiceis set to False in order to exit the loop.
Here we deliberately add a shape Duration (OthersProcess) to create zombie messages. This shape creates a ‘duration’ to simulate the real-life situation of other processing that might follow.
Assume that the first invoice arrives in the Receive, which triggers the orchestration and initializes a correlation. The orchestration’s status will be Dehydrated, pending the receipt of new invoices, or the end of the duration that was set. This means that during the duration set, the following situations can arise:
- no message is sent subsequently;
- new messages with the same correlation sets are received, and the left branch is executed;
- new messages with different correlations sets are received, and new orchestrations are triggered.
At the end of the period, the right branch is executed, thevExistNextInvoicevariable is set to False, the receive loop is exited, and so is the processing. Now, any new message arriving will be a zombie message. It will not be processed and the orchestration will be suspended. The famous “The instance completed without consuming all of its messages”error is returned.
When the orchestration is run at the end of the duration and has just exited the loop, if messages arrive with the same correlation sets, this creates zombie messages.
We suggest two possible solutions to this issue:
- The simple suggestion is to increase the loop duration. This provides a larger window to receive messages.
- Another solution:
The idea is to create another loop that encompasses the ManageNextInvoiceloop, called ZombiesHandle. The condition to exit this loop is the same, vExistNextInvoice = False. When we exit the ManageNextInvoiceloop,
vExistNextInvoice = False
Continue with other processing (OtherProcess). To this end, we add a new Listen shape, with a very short duration, such as one second:
In our left branch, we copy the start of the orchestration, i.e. we receive an invoice, set the duration and run the invoice processing, and send them to terminate. At the end of the left branch of this Listen, we reactivate the ZombiesHandle loop. In the StartNewListen shape, we put:
vExistNextInvoice = True;
In this way, the ManageNextInvoice loop is reactivated and the next sequence of processing can start.
A very short duration is used to capture any new incoming messages. If any arrive, the loop is reactivated and the processes run. Otherwise, the orchestration instance (for the same correlation set) will end very quickly. Even if new messages have arrived in the meantime, a new instance of the orchestration will be triggered. The zombie message phenomenon is thus avoided.
After releasing the project into BizTalk and binding the ports, a few invoice messages can be sent as tests, for example:
In the send folder, we obtained processed invoice messages as follows:
We ran many tests without encountering any zombie messages.
This article therefore explained how zombie messages arise and offered a solution to minimize them.