Receiving algorithm
Because of how the MSMQ API has been designed, the receive algorithm is more complex than other polling-driven transports (such as SQLServer).
The main loop uses GetMessageEnumerator2
to iterate over all messages available in the queue and creates a separate receiving task for each message.
DisableInstaller
The NuGet package includes two PowerShell scripts to help facilitate the queue creation and cleanup for an endpoint. The queues for the endpoint can be created via code by calling the EnableInstaller configuration API. The code to create the queues is run every time the endpoint starts up.
To avoid queues being created when installers are enabled on the endpoint (using endpointConfiguration.
), call this transport configuration API to explicitly not create the queues:
var transport = new MsmqTransport
{
CreateQueues = false
};
endpointConfiguration.UseTransport(transport);
DisableDeadLetterQueueing
Global
Forwarding messages to the Dead Letter Queue (DLQ) is ON by default as a safety mechanism. The DLQ setting prevents message loss due to either bad configuration or any other reason where the message could not be delivered to the destination queue.
Call this API to disable storing undeliverable messages in the DLQ. This setting must only be used where the loss of messages is an acceptable scenario. Writing to the DLQ can add performance overhead. In high volume scenarios, it can be turned off. When doing so, it's essential to ensure that the routing configuration is accurate, as it can lead to message loss otherwise.
var transport = new MsmqTransport
{
UseDeadLetterQueue = false
};
endpointConfiguration.UseTransport(transport);
Per message: (Requires version 1.1+)
The global dead-letter-queueing behavior can be overriden for specific messages via send, publish, or reply options.
Enable:
var options = new SendOptions();
options.UseDeadLetterQueue();
Disable:
var options = new SendOptions();
options.UseDeadLetterQueue(enable: false);
DisableConnectionCachingForSends
By default, caching is set to true and instructs MSMQ to cache connections to a remote queue and re-use them as needed instead of creating new connections for each message. This API allows connection caching to be turned off. However doing so will negatively impact the message throughput in most scenarios.
var transport = new MsmqTransport
{
UseConnectionCache = false
};
endpointConfiguration.UseTransport(transport);
UseNonTransactionalQueues
This setting should be used with caution. As the queues are not transactional, any message that has an exception during processing will not be rolled back to the queue. Therefore this setting must only be used where loss of messages is an acceptable scenario.
While there may be a performance gain when using non-transactional queues, it should be carefully weighed against the possibility of message loss.
var transport = new MsmqTransport
{
UseTransactionalQueues = false
};
endpointConfiguration.UseTransport(transport);
This setting is not the same as using TransactionMode.None. This setting implies that the physical queue where the messages will be stored will not be a transactional queue. Endpoints that use non-transactional queues will not be able to send messages to endpoints that use transactional queues. Therefore it is important for all endpoints that intend to communicate with each other to use the same setting.
EnableJournaling
Global
This API enables the use of journaling messages. With this option, MSMQ will store a copy of every message received and processed in the journal queue.
var transport = new MsmqTransport
{
UseJournalQueue = true
};
endpointConfiguration.UseTransport(transport);
This setting can potentially use up the MSMQ storage quota based on the message volume.
Per message: (Requires version 1.1+)
The global journaling behavior can be overriden for specific messages via send, publish, or reply options.
Enable:
var options = new SendOptions();
options.UseJournalQueue();
Disable:
var options = new SendOptions();
options.UseJournalQueue(enable: false);
TimeToReachQueue
Overrides the time-to-reach-queue (TTRQ) timespan. The default value is Message.InfiniteTimeout.
TTRQ is the time limit for the message to reach the destination queue, beginning from the time the message is sent. This sets the underlying Message.TimeToReachQueue.
var transport = new MsmqTransport
{
TimeToReachQueue = TimeSpan.FromMinutes(15)
};
endpointConfiguration.UseTransport(transport);
MSMQ Label
This feature was added in version 6 and can be used to communicate with version 5 (and higher) endpoints. However it should not be used when communicating with earlier versions (2, 3 or 4) since in those versions the MSMQ Label was used to communicate certain NServiceBus implementation details.
Often when debugging MSMQ using native tools, it is helpful to have some custom text in the MSMQ Label. For example the message type or the message id. As of version 6 the text used to apply to Message.Label can be controlled at configuration time using the ApplyLabelToMessages
extension method. This method takes a delegate which will be passed the header collection and should return a string to use for the label. It will be called for all standard messages as well as audits, errors and all control messages. The only exception to this rule is received messages with corrupted headers. In some cases, it may be useful to use the Headers.
key to determine if a message is a control message. These messages will be forwarded to the error queue with no label applied. The returned string can be String.
for no label and must be at most 240 characters.
var transport = new MsmqTransport
{
// Set the msmq message label to the current Message Id
ApplyCustomLabelToOutgoingMessages = headers => headers[Headers.MessageId]
};
endpointConfiguration.UseTransport(transport);
Transactions and delivery guarantees
MSMQ Transport supports the following Transport Transaction Modes:
- Transaction scope (Distributed transaction)
- Transport transaction - Send atomic with receive
- Transport transaction - Receive only
- Unreliable (Transactions disabled)
See also Controlling Transaction Scope Options.
Transaction scope (distributed transaction)
In this mode, the ambient transaction is started before receiving the message. The transaction encompasses all stages of processing including user data access and saga data access.
MSMQ does not support Snapshot isolation level. Snapshot isolation level is suitable only when persistence is required. This isolation level can be applied only on the persister if the transport and persistence do not share the same transaction. Lower the transport transaction mode to at least Sends atomic with receive, use WrapHandlersInATransactionScope, and pass IsolationLevel.
as a parameter. This has consistency issues as mentioned in the Transport Transactions Consistency Guarantees article
Native transactions
In versions 1.0 and above, the MSMQ transport uses native transactions to support the ReceiveOnly
and SendsAtomicWithReceive
levels. With SendsAtomicWithReceive
the native transaction for receiving messages is shared with sending operations. That means the message receive operation, and any send or publish operations, are committed atomically. When using ReceiveOnly
, the transaction is not shared with sending operations and dispatched messages may not be rolled back in case the receiving transaction needs to abort.
The MSMQ transport in NServiceBus version 6.0 does not distinguish between ReceiveOnly
and SendsAtomicWithReceive
and both levels behave as SendsAtomicWithReceive
.
Unreliable (transactions disabled)
In this mode, when a message is received, it is immediately removed from the input queue. If processing fails, the message is lost because the operation cannot be rolled back. Any other operation that is performed when processing the message is executed without a transaction and cannot be rolled back. This can lead to undesired side effects when message processing fails part way through.