An Alternative Queuing Model
Before talking about the new model for queued transports that we've added to WCF 4.0, let's first look at the traditional transacted receive pattern.
Start a transaction. This can be relatively cheap although we'll have to pay more for the transaction once we actually have a resource (message) and other participants.
Wait for a message to be available. If we don't get a message, abort the transaction we started and try again from the beginning.
Receive the contents of the message under the transaction, removing it from the queue.
Do some work inside the transaction. When all of the participants complete their work, the transaction can complete. A successfully completed transaction finalizes the work. An aborted transaction rolls back the work and rolls back the receive of the message. After aborting a transaction, the corresponding message is back in the queue to try again from the beginning.
Some downsides of the transacted model are that we have to have a transaction the entire time through the loop and that if there are multiple consumers reading from the queue, there's no guarantee that if you abort a transaction then the same instance will get the message the next time. This makes defining error logic in a distributed system more challenging.
Here's a similar barebones description of a model based on locking, which I'll go into more detail for next time as well as talk about the programming interfaces. You'll notice that in the end you get exactly the same outcome, but the changes to how the receive is done have mitigated the downsides.
Wait for a message to be available.
Peek the contents of the message, at the same time locking the message so that no one else sees it as an available message. Peeking receives the contents of the message without removing it from the queue. Leaving the message in place but making it invisible to others is subtly different from removing the message but making it possible to undo the remove. This model exploits that difference.
Start a transaction.
Do some work inside the transaction. When you're done with the message, delete it inside the transaction. When all of the participants complete their work, the transaction can complete. A successfully completed transaction finalizes the work and removes the message from the queue. An aborted transaction rolls back the work and rolls back the delete of the message. After aborting a transaction, you still have the message locked so we can go back to step 3 rather than trying from the beginning.