I'm getting the following exception when trying to respond to a RabbitMQ exclusive queue using Rebus.
- e {"Queue 'xxxx-xxxx' does not exist"} Rebus.Exceptions.RebusApplicationException
+ InnerException {"The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=405, text=\"RESOURCE_LOCKED - cannot obtain exclusive access to locked queue 'xxxx-xxxx' in vhost '/'. It could be originally declared on another connection or the exclusive property value does not match that of the original d...\", classId=50, methodId=10, cause="} System.Exception {RabbitMQ.Client.Exceptions.OperationInterruptedException}
The client declares the queue as exclusive and is able to successfully send the message to the server. The server processes the message but throws the exception when sending the response.
I can see in the Rebus source code (Rebus.RabbitMq.RabbitMqTransport.cs) that it attempts a model.QueueDeclarePassive(queueName) which throws the above exception.
I found the following statement Here
RabbitMQ extends the exclusivity to queue.declare (including passive declare), queue.bind, queue.unbind, queue.purge, queue.delete, basic.consume, and basic.get
Modifying the Rebus source to simply return true from the CheckQueueExistence method allows the response message to be sent. So my question is, is this an issue in Rebus with the use of the passive declare on an exclusive queue, is RabbitMQ blocking the call, or is there a fundamental concept I'm missing?
The reason Rebus does the model.QueueDeclarePassive(queueName) thing, is because it tries to help you by verifying the existence of the destination queue before sending to it.
This is to avoid the situation where a sender goes
using var bus = Configure.With(...)
.(...)
.Routing(r => r.TypeBased().Map<string>("does-not-exist"))
.Start();
await bus.Send("I'M LOST 😱");
and the message is lost.
The problem here is that RabbitMQ still uses a routing key to match the sent message to a binding pointing towards a queue, and if no matching binding exists (even when using the DIRECT exchange type) the message is simply routed to 0 queues, and thus it is lost.
If you submit a PR that makes it configurable whether to check that destination queues exist, then I'd be happy to (help you get it right and) accept it.
Related
RabbitMq 3.8.5, C# RabbitMqClient v6.1.0, .Net Core 3.1
I feel that I'm misunderstanding something with RabbitMq so I'm looking for clarification:
If I have a client sending a message to an exchange, and there's no consumer on the other side, what is meant to happen?
I had thought that it should sit in a queue until it's picked up, but the issue I've got is that, right now there is no queue on the other end of the exchange (which may well be my issue).
This is my declaration code:
channel.ExchangeDeclare(name, exchangeType, durable, autoDelete);
var queueName = ret._channel.QueueDeclare().QueueName;
channel.ConfirmSelect();
and this is my publisher:
channel.BasicPublish(exchangeName, routingKeyOrTopicName, messageProperties, message);
However doing that gives me one queue name for the outbound exchange, and another for the inbound consumer.
Would someone help this poor idiot out in understanding how this is meant to work? What is the expected behavior if there's no consumer at the other end? I do have an RPC mechanism that does work, but wasn't sure if that's the right way to handle this, or not.
Everything works find if I have my consumer running first, however if I fire up my Consumer after the client, then the messages are lost.
Edit
To further clarify, I've set up a simple RPC type test; I've two Direct Exchanges on the client side, one for the outbound Exchange, and another for the inbound RPC consumer.
Both those have their own queue.
Exchange queue name = amq.gen-fp-J9-TQxOJ7NpePEnIcGQ
Consumer queue name = amq.gen-wDFEJ269QcMsHMbAz-t3uw
When the Consumer app fires up, it declares its own Direct exchange and its own queue.
Consumer queue name = amq.gen-o-1O2uSczjXQDihTbkgeqA
If I do it that way though, the message gets lost.
If I fire up the consumer first then I still get three queues in total, but the messages are handled correctly.
This is the code I use to send my RPC message:
messageProperties.ReplyTo = _rpcResponder._routingKeyOrTopicName;
messageProperties.Type = "rpc";
messageProperties.Priority = priority;
messageProperties.Persistent = persistent;
messageProperties.Headers = headers;
messageProperties.Expiration = "3600000";
Looking at the management GUI, I see that all three queues end up being marked as Exclusive, but I'm not declaring them as such. In fact, I'm not creating any queues myself, rather letting the Client library handle that for me, for example, this is how I define my Consumer:
channel.ExchangeDeclare(name, exchangeType, durable, autoDelete);
var queueName = ret._channel.QueueDeclare().QueueName;
Console.WriteLine($"Consumer queue name = {queueName}");
channel.QueueBind(ret.QueueName, name, routingKeyOrTopicName, new Dictionary<string, object>());
In RabbitMQ, messages stay in queues, but they are published to exchanges. The way to link an exchange to a queue is through bindings (there are some default bindings).
If there are no queues, or the exchange's policy doesn't find any queue to forward the message, the message is lost.
Once a message is in a queue, the message is sent to one of that queue's consumers.
Maybe you're using exclusive queues? These queues get deleted when their declaring connection is gone.
Found the issue: I was allowing the library to generate the queue names rather than using specific ones. This meant that RabbitMq was always having to deal with a shifting target each time.
If I use 'well defined' queue names AND the consumer has fired up at least once to define the queue on RabbitMq, then I do see the message being dropped into the queue and stay there, even though the consumer isn't running.
ActiveMQ NMS consumer (C#) can't able to receive old messages: My C# program will be in a while loop operating on message received.
I'm establishing a NMS consumer connection each time when I need a message and operate on the message received.
The problem is whenever I start the program the messages posted after my programs 1st connection attempt, I can get them downloaded/consumed.
However, if no messages are flowing in and I have some old messages sitting before I establish 1st connection, those messages are not getting consumed. I used proper connection.start(). and I'm using consumer.receive(0) 0 - waittime.
NNS consumer example contains the following line:
// Consume a message
ITextMessage message = consumer.Receive() as ITextMessage;
When running this code it may look as if consumer is returning null (but it doesn't).
Problem here is that consumer.Receive() returns an IMessage which is not always of type ITextMessage. In my case it was returning ActiveMQBytesMessage which is completely different type, and converting it to ITextMessage in as ITextMessage returns null.
The following code would be more appropriate as an example:
// Consume a message
IMessage message = consumer.Receive();
Any time you call a consumer receive with no timeout you are running the risk of not getting a message and must be prepared for that. The consumer doesn't query the broker for a message on a call to receive so if there hasn't been a message dispatched or it's still in flight then the receive will return null message.
Creating a new connection on each attempt to get a message is really an anti-pattern and you should consider using a long lived connection / consumer to avoid this situation more, although you can't completely mitigate this as you are doing a receive(0).
How to configure MassTransit to retry context.Publish() before failing, for example when RabbitMQ server is temporary unavailable?
The problem with retry in this context is that the only real reason a Publish call would fail is if the broker connection was lost (for any reason: network, etc.).
In that case, the connection which was used to receive the message is also lost, meaning that another node connected to the broker may have already picked up the message. So a retry in this case would be bad, since it would reconnect to the broker and send, but then the message could not be acknowledged (since it was likely picked up on another thread/worker).
The usual course of action here is to let it fail, and when the receive endpoint reconnects, the message will be redelivered to a consumer which will then call Publish and reach the desired outcome.
You should make sure that your consumer can handle this (search for idempotent) properly to avoid a failure causing a break in your business logic.
Updated Jan 2022: Since v7, MassTransit retries all publish/send calls until the cancellationToken is canceled.
In older versions of Rebus you could control the error queue. But now you only have a "inputQueue" in the azure servicebus extender. How can I control the error queue?
Bus = Configure.With(_adapter)
.Transport(t => t.UseAzureServiceBus(ConnectionString, inputQueue /*, errorQueue */))
.Start();
UPDATE: they end up in the "error" queue. I have now messages from different sources in the same (error)queue. So, the question became, can rebus filter out messages where the input queue matches the custom property rbs2-source-queue?
The error queue is still configurable!
You made me realize that this was not something that I had mentioned on the wiki though, so I just went and added it :)
The solution to configuring which error queue to use is pretty simple - check this out:
Configure.With(...)
.Options(b => b.SimpleRetryStrategy(errorQueueAddress: "somewhere_else"))
.(...)
As you have correctly discovered, the rbs2-source-queue header reveals which input queue the message failed too many times in, and therefore it can be used for filtering the failed messages later on. There's no way, though, to only receive those messages that have a specific value in that header.
I am using WMQ to access an IBM WebSphere MQ on a mainframe - using c#.
We are considering spreading out our service on several machines, and we then need to make sure that two services on two different machines cannot read/get the same MQ message at the same time.
My code for getting messages is this:
var connectionProperties = new Hashtable();
const string transport = MQC.TRANSPORT_MQSERIES_CLIENT;
connectionProperties.Add(MQC.TRANSPORT_PROPERTY, transport);
connectionProperties.Add(MQC.HOST_NAME_PROPERTY, mqServerIP);
connectionProperties.Add(MQC.PORT_PROPERTY, mqServerPort);
connectionProperties.Add(MQC.CHANNEL_PROPERTY, mqChannelName);
_mqManager = new MQQueueManager(mqManagerName, connectionProperties);
var queue = _mqManager.AccessQueue(_queueName, MQC.MQOO_INPUT_SHARED + MQC.MQOO_FAIL_IF_QUIESCING);
var queueMessage = new MQMessage {Format = MQC.MQFMT_STRING};
var queueGetMessageOptions = new MQGetMessageOptions {Options = MQC.MQGMO_WAIT, WaitInterval = 2000};
queue.Get(queueMessage, queueGetMessageOptions);
queue.Close();
_mqManager.Commit();
return queueMessage.ReadString(queueMessage.MessageLength);
Is WebSphere MQ transactional by default, or is there something I need to change in my configuration to enable this?
Or - do I need to ask our mainframe guys to do some of their magic?
Thx
Unless you actively BROWSE the message (ie read it but leave it there with no locks), only one getter will ever be able to 'get' the message. Even without transactionality, MQ will still only deliver the message once... but once delivered its gone
MQ is not transactional 'by default' - you need to get with GMO_SYNCPOINT (MQ transactions) and commit at the connection (MQQueueManager level) if you want transactionality (or integrate with .net transactions is another option)
If you use syncpoint then one getter will get the message, the other will ignore it, but if you subsequently have an issue and rollback, then it is made available to any getter (as you would want). It is this scenario where you might see a message twice, but thats because you aborted the transaction and hence asked for it to be put back to how it was before the get.
I wish I'd found this sooner because the accepted answer is incomplete. MQ provides once and only once delivery of messages as described in the other answer and IBM's documentation. If you have many clients listening on the same queue, MQ will deliver only one copy of the message. This is uncontested.
That said, MQ, or any other async messaging for that matter, must deal with session handling and ambiguous outcomes. The affect of these factors is such that any async messaging application should be designed to gracefully handle dupe messages.
Consider an application putting a message onto a queue. If the PUT call receives a 2009 Connection Broken response, it is unclear whether the connection failed before or after the channel agent received and acted on the API call. The application, having no way to tell the difference, must put the message again to assure it is received. Doing the PUT under syncpoint can result in a 2009 on the COMMIT (or equivalent return code in messaging transports other than MQ) and the app doesn't know if the COMMIT was successful or if the PUT will eventually be rolled back. To be safe it must PUT the message again.
Now consider the partner application receiving the messages. A GET issued outside of syncpoint that reaches the channel agent will permanently remove the message from the queue, even if the channel agent cannot then deliver it. So use of transacted sessions ensures that the message is not lost. But suppose that the message has been received and processed and the COMMIT returns a 2009 Connection Broken. The app has no way to know whether the message was removed during the COMMIT or will be rolled back and delivered again. At the very least the app can avoid losing messages by using transacted sessions to retrieve them, but can not guarantee to never receive a dupe.
This is of course endemic to all async messaging, not just MQ, which is why the JMS specification directly address it. The situation is addressed in all versions but in the JMS 1.1 spec look in section 4.4.13 Duplicate Production of Messages which states:
If a failure occurs between the time a client commits its work on a
Session and the commit method returns, the client cannot determine if
the transaction was committed or rolled back. The same ambiguity
exists when a failure occurs between the non-transactional send of a
PERSISTENT message and the return from the sending method.
It is up to a JMS application to deal with this ambiguity. In some
cases, this may cause a client to produce functionally duplicate
messages.
A message that is redelivered due to session recovery is not
considered a duplicate message.
If it is critical that the application receive one and only one copy of the message, use 2-Phase transactions. The transaction manager and XA protocol will provide very strong (but still not absolute) assurance that only one copy of the message will be processed by the application.
The behavior of the messaging transport in delivering one and only one copy of a given message is a measure of the reliability of the transport. By contrast, the behavior of an application which relies on receipt of one and only one copy of the message is a measure of the reliability of the application.
Any duplicate messages received from an IBM MQ transport are almost certainly going to be due to the application's failure to use XA to account for the ambiguous outcomes inherent in async messaging and not a defect in MQ. Please keep this in mind when the Production version of the application chokes on its first duplicate message.
On a related note, if Disaster Recovery is involved, the app must also gracefully recover from lost messages, or else find a way to violate the laws of relativity.