Friday, March 30, 2012

please help me figure out how best to approach my queue problem

Hi,

I am moving my system from a custom created queue to a service broker queue.

I wish to have two queues :

one to hold processing messages
and one to hold emailing messages.

I do not understand why i would have to have an initiator queue and a target queue for each of the above. I have arranged my sql as follows:

-- processing queue
CREATE MESSAGE TYPE ProcessingMessage VALIDATION = NONE
CREATE CONTRACT ProcessingContract (ProcessingMessage SENT BY INITIATOR)
CREATE QUEUE ProcessingQueue
CREATE SERVICE ProcessingService ON QUEUE ProcessingQueue (ProcessingContract)
GO

-- emailing queue
CREATE MESSAGE TYPE EmailingMessage VALIDATION = NONE
CREATE CONTRACT EmailingContract (EmailingMessage SENT BY INITIATOR)
CREATE QUEUE EmailingQueue
CREATE SERVICE EmailingService ON QUEUE EmailingQueue (EmailingContract)
GO

So basically EmailingQueue plays the role of the initiator and the target ( sends messages to itself )... and so does the ProcessingQueue.

I enqueue my messages with the following SP:

PROC [dbo].[queue_enqueue] (
@.fromService SYSNAME,
@.toService SYSNAME,
@.onContract SYSNAME,
@.messageType SYSNAME,
@.entryId int)
AS
BEGIN
DECLARE @.conversationHandle UNIQUEIDENTIFIER
DECLARE @.error int

BEGIN DIALOG @.conversationHandle
FROM SERVICE @.fromService
TO SERVICE @.toService
ON CONTRACT @.onContract
with LIFETIME = 6000, ENCRYPTION = off;

SEND ON CONVERSATION @.conversationHandle Message Type @.messageType (@.entryId)

END

I do the enqueueing with
[queue_enqueue] 'ProcessingService', N'ProcessingService', 'ProcessingContract', 'ProcessingMessage', 1

I dequeue my messages with the following SP:
PROC [dbo].[queue_dequeue]
@.queue SYSNAME,
@.entryId int OUTPUT
AS
BEGIN
DECLARE @.conversationHandle UNIQUEIDENTIFIER;
DECLARE @.messageTypeName SYSNAME;
DECLARE @.conversationGroupId UNIQUEIDENTIFIER;
get conversation group @.conversationGroupId from ProcessingQueue

if ( @.conversationGroupId is not null )
BEGIN
if (@.queue='ProcessingQueue')
RECEIVE TOP(1) @.entryId = CONVERT(INT, [message_body]), @.conversationHandle = [conversation_handle], @.messageTypeName = [message_type_name] FROM ProcessingQueue where conversation_group_id = @.conversationGroupId
else if (@.queue='EmailingQueue')
RECEIVE TOP(1) @.entryId = CONVERT(INT, [message_body]), @.conversationHandle = [conversation_handle], @.messageTypeName = [message_type_name] FROM EmailingQueue where conversation_group_id = @.conversationGroupId
END
END

I dequeue by calling something like:
declare @.entryId int
exec [queue_dequeue] 'ProcessingQueue', @.entryId output

The above works however I have a few issues that I am having problems figuring out.

1. I don't want to "fire and forget". I want to close conversation at initiator endpoint only when target has closed conversation.
2. I want to know how to properly close conversations at initiator and target.
3. I am not polling with while loop or wait for, because of the way my system ( higher up ) is setup, I simply want to enqueue one message, and dequeue one message. If an error occurs in enqueueing or dequeueing I want to raise that error.
4. How do I handle poison messages?
5. Is it better for me to use two queues ( initiator and target ) for each of my queues above?

Can someone please help me with these issues?

thanx

Are you sure the code is correct? You are retrieving the conversation group from one queue, but potentially receive from another. Besides, GET CONVERSATION GROUP locks the conversation solely for the duration of the transaciton, yif you don't wrap the call to [queue_dequeue] in a transaction, you can have errors if multiple threads are calling this procedure. Also, using GET CONVERSATION GROUP is recommended solely for the cases when the application needs to look up some application specific state (corellated with the converstion_group_id) before actually RECEIVEing the messages. Otherwise one can simply go ahead and call RECEIVE verb directly.

The Service Broker queues are not identical with the Computer Science 'queue' data structures (i.e. a FIFO list). They are more like message storage areas for services and services are endpoints for distributed applications, communicating through messages. As such, using Service Broker queues probably won't map exactly 1-to-1 with your previous user-table-queue code.

You already noticed that you now have to call END CONVERSATION. I'm not sure how the rest of the system works: are you reusing converstions when enqueueing? is each enqueue a separate dialog?. Probably the [queue_dequeue] would have to call END CONVERSATION to end the conversations on which it received the current message. As long as you keep initiator and target on the same queue, this means that the same [queue_dequeue] procedure will have to handle the EndDialog messages (that would be one reason why you should split the queues).

|||Hi Remus, thanks for your reply!

I admit, I used the GET CONVERSATION GROUP when i got desperate.. thought that RECEIVE wasn't locking the queue entry on its own. I'll get rid of it since we don't really need to reuse conversations, we don't have a message intensive system, we are mostly worried about message intergrity and reduced processor load as compared to our previous system.

So, I gather it is better for me to setup my queues in the service broker conventional way, two queues for each physical queue that I have.

I still dont understand fully how the "dont fire and forget" method works:

1. Initiator starts dialog and sends message. does not end dialog.
2. Target receives message and ends dialog which in turn sends EndDialog message to initiator.
3. Initiator activation SP recieves EndDialog message and ends dialog on initiator side?

am I missing something?

thanx once again for your help.

No comments:

Post a Comment