Most probable reason is due to multiple concurrent receivers where between peek and receive, another consumer calls receive() rendering current receive to pickup the next message instead. Your code worked as expected for me when using a single listener with a non-partitioned queue. Another reason can be the queue is 'partitioned' where peek and receive picked message from different fragments.
Modifying your code a bit like below should solve your problem in concurrent competing consumers scenario. Find inline comments to explain.
var msgClient = clientMessagingFactory.CreateQueueClient(QueueClient.FormatDeadLetterPath(sourceQueue.Name), ReceiveMode.PeekLock);
var message = msgClient.Receive();
if (message != null)
{
// we got here means, there was one active message found and now message is locked from others view
if (message.MessageId == "xxx")
{
//with id 'xxx', defer
message.Defer();
}
// regardless of condition match, abandoning
message.Abandon();
}
else
{
// got here means there was no active Message above
// now see if we have deferred Message
var peeked = msgClient.Peek();
// The reason of checking state here again, in case there was new message arrived in the queue by now
if (peeked != null && peeked.MessageId == "xxx" && peeked.State == MessageState.Deferred)
{
try
{
// now receive it to lock
// yes, you can receive 'deferred' message by sequence number, but not 'active' message :)
message = msgClient.Receive(peeked.SequenceNumber);
// Perform some operation with deferred state message
message.Complete(); // don't forget it
}
catch (MessageNotFoundException)
{
// just in case message was peeked by competing consumer and received
}
}
}
Note: The peek operation on a non-partitioned entity always returns the oldest message, but not on a partitioned entity. Instead, it returns the oldest message in one of the partitions whose message broker responded first. There is no guarantee that the returned message is the oldest one across all partitions.