Message absolute location URI

Jul 6, 2015 at 9:11 PM
How to get the message location URI?

My goal is to use location URI as follows:
  • HttpClient.DeleteAsync(messageLockUri)
    or
  • HttpClient.PutAsync(messageLockUri, content)
Coordinator
Jul 7, 2015 at 1:12 AM
Can you be more specific about what you are trying to do? What is the broker/service you are using? What operations are you trying to do against that broker/service using HttpClient? How is that operation related to the AMQP Lite library?

Thanks.
Jul 7, 2015 at 6:12 PM
I am trying to delete specific message from a queue based on its URI using Http protocol but I have first to pass Uri of message location for other service.
public async Task<Uri> receiveMessage() {
     var receiver = new ReceiverLink(session, "receiver-link", entity);
     var message = await receiver.ReceiveAsync();
     receiver.Accept(message);
     // extract somehow message URI -> messageLockUri 
     await receiver.CloseAsync();
     return messageLocUri
}
public Task deleteMessageFromQueue(Uri messageLockUri)
{
      HttpClient httpClient = new HttpClient();
      var wr = await httpClient.DeleteAsync(messageLockUri);  
}
static void Main(string[] args)
{
      var uri = receiveMessage();
      /*
         rest of the program
      */
      deleteMessageFromQueue(uri);
}
Coordinator
Jul 8, 2015 at 4:59 PM
If I understand you correctly, you want to receive a message with AMQP Lite, get the message lock Uri, and delete the message using REST/HTTP protocol? Is that correct? Are you using the Service Bus queue?

After you receive a message and call:
receiver.Accept(message);

a delete message command is sent to the service, and in most of the cases, the message will be deleted. I say "in most of the cases" because Accept method currently does a fire-and-forget message delete. If an error occurs on the transport or in the broker, the client will not get a nack for the call. This results in a potential redelivery of the same message to the client. It is possible to block the Accept call until a response is received from the service, but it is not currently supported by the client.

If you have to delete the message using HTTP, why not also receive with HTTP?
Jul 9, 2015 at 9:50 AM
Thank you for your reply. In my case I use Azure Service Bus queues, and my goal is to receive the message using AmqpLite. However the deletion of message will be done by different service using different protocols, ex. Rest/Http protocol or could be another Amqp.
Your reply made me to realize that I have to use "Release" instead of "Accept" to avoid message deletion at this stage as follows:
     receiver.Release(message); //message will stay in the queue
     // extract somehow message URI -> messageLockUri 
     await receiver.CloseAsync();
     return messageLocUri
but still I need something generic across the different protocols to get the access to the message location, for instance an URI.
Coordinator
Jul 9, 2015 at 9:01 PM
This scenario does not work today with the combination of the Azure Service Bus and Amqp.Net Lite.

To delete a message, the message must be still locked at the broker. If you call Release the message is unlocked on the broker, so even if you have the message lock location, the HTTP DELETE call will fail with an error.

If you do not call Release from Amqp Lite (to keep the message locked), the library will stop getting more messages after a while because the queue for tracking the state of received messages is full.

The data to construct the lock location is available, though.
            ReceiverLink receiver = new ReceiverLink(session, "recv-link", entity);
            message = receiver.Receive();
            long sn = (long)message.MessageAnnotations[new Symbol("x-opt-sequence-number")];
            Guid lockToken = new Guid(message.DeliveryTag);
            string lockLocation = string.Format("https://{0}/{1}/mesages/{2}/{3}", host, entity, sn, lockToken);
But given the above restrictions, this is not useful.
Jul 10, 2015 at 6:34 PM
Edited Jul 10, 2015 at 6:35 PM
Thank you for your reply it was helpful. And I want to extend my inquiry to ask if we can use lockLocation to reconstruct a ReceiverLink to receive such the message as follows:

void Complete (string lockLocation )
{
     ReceiverLink receiver //use LockLocation to initialize receiver
     var message = receiver.Receive(); //receive the message located at LockLocation
     receiver.Accept(message);
}
Coordinator
Jul 11, 2015 at 12:56 AM
Unfortunately this is not possible. The messages delivery state is associated with the link (receiver). When you use the lock token on another receiver, it is no longer valid to that link.

The Service Bus queue supports receive by sequence number.
https://msdn.microsoft.com/en-us/library/hh322665.aspx

This feature is not currently supported by the AMQP protocol. We are adding this support. With that it will be possible to delete a message just using the sequence number.
Jul 11, 2015 at 8:28 AM
Is there is a temporary workout in order to use different links(receivers)
            ReceiverLink receiver1 = new ReceiverLink(session, "recv-link1", entity);
            var message1 = receiver1.Receive(); //receive message1
            receiver1.Release(message1);
            await receiver1.CloseAsync();

            //initialize new link in different scope but we hold the same message 
            ReceiverLink receiver2 = new ReceiverLink(session, "recv-link2", entity);
            receiver2.Accept(message1); //delete message1
            await receiver2.CloseAsync();
Coordinator
Jul 13, 2015 at 5:32 PM
There are a couple of issues:
  1. you cannot Release the message after receiving it with receiver1. The message will become available and redelivered to the client right away. If you have a receive loop or message listener, you will get the same message again.
  2. the received message can only be deleted/released by the link where it was received.
If you can pass the message object down, can you also pass the receiver1 object together so you can delete the message later? This may work but I don't think it solves the original problem where you want to decouple message receive and message delete, possibly even with different protocols.

I am not sure if you could use the following approach to achieve something similar.
  1. Create a receiver link and receive messages. This is your normal receiver.
  2. After you process the message, call receiverLink.Reject(message) to deadletter the message. In the SB, the message will be moved to "$DeadLetterQueue" (a sub-queue of the main queue).
  3. In your downstream component, delete the messages from "$DeadLetterQueue". To do this, you could use AMQP or HTTP.
    3.1 AMQP: create a receiver link listening on "mainQueue/$DeadLetterQueue", and call Accept to delete each received message.
    3.2 HTTP: https://msdn.microsoft.com/en-us/library/azure/hh780762.aspx
    Just be aware that this approach will double the network I/O.
Jul 13, 2015 at 7:49 PM
Edited Jul 13, 2015 at 7:49 PM
Hi Xin,

it is important in this scenario to be able to deal with some kind of message lock URI or similar.
According to SB specification, it is expectable to have some kind of message handle. In HTTP/REST is this LockURI. However more general purpose handle might be implicitly derived from message sequence number. Unfortunately this is not supported right now.
The idea behind all this is to have a "message handle" and not the whole message.

so we can use:

api.Complete("msgHandle");

Ideally, the same "message handle" should be accessible across all protocols.

Do you see any way to create all you need internally from Message Lock URI?
It would be important to have it.

Do you see right now any workaround, which could decouple us from ReceiverLink and/or message?

Thanks
Damir