views:

716

answers:

3

This is the setup for the queue:

  1. The queue is public and marked as transactional.
  2. A web service is responsible for inserting messages into the queue.
  3. A Windows service is listening in on the queue

Given that, how can I only process messages that are at least 20 minutes old?

A: 

The caveat of this is at what point are you defining 20 minutes old?

Case 1: If defined based upon when the message was generated and sent, then you're going to have to use the Message.SentTime. You can then use Peek to look at the message, and delay processing until the 20 minute threshold has been met. This assumes that messages will arrive in a linear order, which is not guaranteed! (See below.)

Case 2: If defined based upon when the message arrives in the queue, take note of the time of Message.ArrivedTime (IE process is "listening" on the message queue) and consume 20 minutes later.

But... Both cases cause a problem as you can only peek on the first message. As such, you will have to consume the message into a queue or other structure for processing at the designated time.

Gavin Miller
+1  A: 

Given that a queue is first-in, first-out, you have to assume that all messages that are in a queue arrive at a time equal to or later than the time than the first message in the queue.

You can use that to your advantage.

Basically, you would call the Peek method on your MessageQueue instance and look at the ArrivedTime property. If the time between now, and the arrived time is greater than 20 minutes, then you would process it, otherwise, you would continue to wait (until the next time you process the messages on the queue).

Here is an example:

static Message GetQueueMessage(MessageQueue queue)
{
  // Store the current time.
  DateTime now = DateTime.Now;

  // Get the message on top of the queue.
  Message message = queue.Peek();

  // If the current time is greater than or equal to 20 minutes, then process it,
  // otherwise, get out and return false.  Generate 20 minutes first.
  // You can refactor this to have it passed in.
  TimeSpan delay = TimeSpan.FromMinutes(20);

  // If the delay is greater than the arrived time and now, then get out.
  if (delay > (now - message.ArrivedTime))
  {
    // Return null.
    return null;
  }

  // Pop the message from the queue to remove it.
  return queue.ReceiveById(message.Id);    
}

You can then run this in a loop, waiting for a bit in between (every five seconds, ten, whatever you feel is appropriate for the frequency of processing):

// Have some other reasonable check here.
do while (true)
{
  // The message to process.
  Message message = GetQueueMessage(queue);

  // Continue to process while there is a message.
  do while (message != null)
  {
    // Process the message.

    // Get the next message.
    message = GetQueueMessage(queue);
  }

  // Wait a little bit, five seconds for example.
  Thread.Sleep(5000);
}

Ideally, based on Charles Conway's comment, you would not process this in a loop, but rather, just call GetQueueMessage from an event handler on a timer which would fire on your designated interval.

casperOne
A: 
// Have some other reasonable check here.
do while (true)
{
...
}

Having "true" in a boolean condition is a code smell... Have you considered using a timer the wakes every 5 seconds?

Timer timer = new Timer();
timer.Elapsed +=new ElapsedEventHandler(timer_Elapsed);

static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
   // The message to process.
   Message message = GetQueueMessage(queue);

    // Continue to process while there is a message.
   do while (message != null)
   {
   // Process the message.

   // Get the next message.
   message = GetQueueMessage(queue);
   }

}
Chuck Conway