May 4, 2008

This time, we will look into an inherent race condition that can occur in a workflow service which communicates with the outside world in a duplex fashion.

If your workflow wants to communicate with an external WCF service in a duplex fashion, you’ll very quickly find that you need a SendActivity to send a message to the outside world, and a ReceiveActivity to receive the reply. Arranging these activities in your workflow, you might come up with the following result:

Everything works perfectly, and you are convinced that this is the way to go about implementing a duplex workflow service.

Dramatic suspense.

Now imagine that between the send and the receive operations there is some additional processing that you might require. For example, you might add a code activity that emulates some processing:

Everything still works perfectly. Even if the external service wants to talk to the workflow before the code activity completes, the message is queued and processed when the receive activity executes.

What does he want, you might ask. What’s the difference between a delay activity and just another code activity that does some processing? Well, apparently there is a difference.

What happens now is that if the external service sends a message to the workflow while the workflow is inside the delay activity, the message is transparently swallowed and lost. It never reaches the ReceiveActivity. Yes, never. And there is no exception to let you know this happened.

You can reproduce this curious scenario yourself by downloading the sample Visual Studio 2008 solution, enabling the DelayActivity and seeing for yourself that the workflow never completes if the delay activity is running while the external message is being enqueued.

Workaround? Let’s consider what we need to happen. We need the receive activity to be executing already when the workflow is going into the delay activity. How can we do that? We need a ParallelActivity. The left branch (executed first) will be the ReceiveActivity, and the right branch (executed next) will be the SendActivity and all the rest:

And now all of the sudden everything falls in its place, and the receive activity receives the message after the delay activity completes.

Since you can never be sure (unless you’re writing a very very simple workflow) that you won’t end up having a DelayActivity before the receive operation, you should always use this paradigm to write a duplex workflow service.