Skip to main content

Message identity and IQueueMessageWithId

The IQueueMessage interface doesn't contain any properties so you can define your message in any way you want. But using IQueueMessage means that the system won't have any knowledge of message identity. The system won't compare two messages to see if they are identical to prevent duplicates in a queue.

If you need to ensure that a queue never contains duplicates of the same message you should instead use the IQueueMessageWithId interface.

The only property that you're forced to have on your message class is the Id property. What the id should be depends on your queue. If you want you can use a combination of values in the implementation of the property. The property is allowed to return null so you can have messages in the same queue where some have identity and others don't.

If we take the SendOrderConfirmationQueueMessage from the example in the Overview we can define it like this instead:

[Queue("send_order_confirmation")]
public class SendOrderConfirmationQueueMessage : IQueueMessageWithId
{
public string? Id => OrderId;
public string OrderId { get; set; }
public string Email { get; set; }
}

By doing this the system will ensure that if we already have a SendOrderConfirmationQueueMessage message for order with id 123 we will never allow duplicates to be enqueued. If someone enqueues it again we'll instead update the existing message rather than adding another one. This is true regardless of which status the message has.

Other queue systems has a separate deadletter queue where failed messages are placed which can allow duplicates to still happen. But if we have a message for order 123 in the Error status and someone enqueues a message with that id the existing message gets updated and the status is set to Pending again. Read more about message statuses here.

Enqueueing while a message is leased​

For database and in-memory queues, enqueueing a new version of a leased message sets its status to AwaitingRelease. The current processor keeps its original lease, and no other processor can lease the newer version yet.

The latest enqueue supplies the payload, priority, processing date, and StatusAfterRelease. Further enqueues replace those deferred values. When the current processor releases or finishes the message, Nexus applies that destination. Immediate messages become Pending, future messages remain Sleeping, and custom destinations retain their custom status. If a sleeping message's processing date has already passed, normal scheduled-message readiness rules apply immediately.

The older processor's payload updates, priority, rescheduling, or failure status do not overwrite the newer enqueue. Successful completion still records the processed version as the previous message, even when processed-message retention is zero. If the lease expires instead, normal abandonment and RetryAbandonedMessages rules apply.

This behavior does not apply to external pending storage, which retains its broker-specific delivery behavior.

Idempotent messages​

If you use message identity you might also want to use message idempotency. It tells Nexus to discard incoming messages where the message property valies are exactly the same as a message with the same id in the queue. Which is handy if you only want to process changes but the queue is populated with data from a system that only allows full exports and can't tell you which messages/entities have changed since the last sync.