Friday, March 30, 2012

RECEIVING from QUEUE by ConversationHandle

Is it possible to receive from a queue by a conversation handle? In the documentation there is an example that show you how to do it. Yet, if you "read" the whole document it says that the conversation handle can not be an expression.

The WHERE clause of the RECEIVE statement may only contain search conditions that use conversation_handle or conversation_group_id. The search condition may not contain any of the other columns in the queue. The conversation_handle or conversation_group_id may not be an expression.

Here is what I'm trying to do:

;RECEIVE TOP(1) @.MsgBody = CAST(message_body as XML)

FROM ProcessingLetters

WHERE conversation_handle = @.Conversation_Handle

It doesn't seem to matter if I use RECEIVE or SELECT. It will return nothing.

I've even tried this:

where cast(Conversation_Handle as varchar(100)) = cast(@.Conversation_Handle as varchar(100))

Why am I doing this? I've put something into the queue to let me know that something is processing. When it is done I want to pull it out and end the conversation.

So is the WHERE conversation_handle = @.Conversation_Handle supposed to work?

Thanks.

Trish wrote:

So is the WHERE conversation_handle = @.Conversation_Handle supposed to work?

It does work, but a message with the conversation handle value (@.Conversation_Handle) has to exist in the queue in order to be RECEIVEd. It seems you are asking for a conversation handle that does not have any message ready to be received. You can look into queue using SELECT * FROM [ProcessingLetters] to see what messages are available for RECEIVE in the in the first place.

HTH,
~ Remus

|||

When I do a SELECT * FROM ProcessingLetters it does have a record in it and it has the conversationhandle that I'm looking for.

|||

Make sure that the conversation is the same, often is confusing that the initiator and target handle of the same conversation are sequential guids and look identical.

HTH,
~ Remus

|||The record that is in the queue has a field named conversation_handle. In that field is a GUID. That GUID is 598C2A9E-EE11-DB11-ABC7-00150042E356. This is what is in my variable that I'm using in the WHERE clause:

568C2A9E-EE11-DB11-ABC7-00150042E356.

They sure look the same to me. But I'm becoming more convinced I'm just not doing something right.

|||One starts with 598, one starts with 568.|||

Ok...here is a complete test senerio. It was working until I passed the conversation handle into a stored procedure (exec SB_FinalizeQBForLetterGeneration 0,@.Conversation_Handle_To_Start_Processing,null). It will create everything and then drop everything when it's done. All I did was copy the code into a stored procedure and called the stored procedure...then it quit working.

use adventureworks

-- Drop stored procedure if it already exists

IF EXISTS (

SELECT *

FROM INFORMATION_SCHEMA.ROUTINES

WHERE SPECIFIC_SCHEMA = N'dbo'

AND SPECIFIC_NAME = N'SB_FinalizeQBForLetterGeneration'

)

DROP PROCEDURE dbo.SB_FinalizeQBForLetterGeneration

GO

CREATE PROCEDURE dbo.SB_FinalizeQBForLetterGeneration

@.Result int,

@.ProcessingHandle uniqueidentifier,

@.FailureText nvarchar(256)

AS

DECLARE @.MsgBody XML

;RECEIVE TOP(1) @.MsgBody = CAST(message_body as XML)

FROM ProcessingLetters

WHERE conversation_handle = @.ProcessingHandle

print 'receiving letter from in processing'

if @.MsgBody is null

BEGIN

print 'conversation handle was not found - '+cast(@.ProcessingHandle as varchar(50))

END

ELSE

BEGIN

print 'conversation handle FOUND!!!! '

END

GO

CREATE MESSAGE TYPE SubmitQB VALIDATION = WELL_FORMED_XML;

CREATE MESSAGE TYPE ProcessQB VALIDATION = WELL_FORMED_XML;

CREATE MESSAGE TYPE SubmitGrouping VALIDATION = WELL_FORMED_XML;

CREATE CONTRACT QBLetterCreation (SubmitQB SENT BY INITIATOR, ProcessQB SENT BY INITIATOR, SubmitGrouping SENT BY INITIATOR)

CREATE QUEUE WaitingQBs WITH STATUS=ON

CREATE QUEUE ProcessingLetters WITH STATUS=ON

CREATE QUEUE WaitingToGroup WITH STATUS=ON

CREATE SERVICE QBWaiting ON QUEUE WaitingQBs (QBLetterCreation)

CREATE SERVICE QBLetterProcessing ON QUEUE ProcessingLetters (QBLetterCreation)

CREATE SERVICE LetterGrouping ON QUEUE WaitingToGroup (QBLetterCreation)

-- add to the WaitingQBs queue

BEGIN TRANSACTION

DECLARE @.conversationHandle UNIQUEIDENTIFIER

BEGIN DIALOG CONVERSATION @.conversationHandle

FROM SERVICE QBWaiting

TO SERVICE 'QBWaiting'

ON CONTRACT QBLetterCreation

WITH ENCRYPTION=OFF;

-- Send a message on the conversation

DECLARE @.message nvarchar(max), @.xmlmsg XML

SET @.message = '<createletter><info QBGroupID="111111" Who="111" Why="jest testing" Retries="0"/></createletter>' ;

SET @.xmlmsg = CAST(@.message as XML)

;SEND ON CONVERSATION @.conversationHandle MESSAGE TYPE SubmitQB(@.xmlmsg)

COMMIT TRANSACTION

--

END CONVERSATION @.conversationHandle

--

-- retrieve from waiting qbs

DECLARE @.Conversation_Handle_For_Waiting_QB uniqueidentifier;

DECLARE @.MsgBody XML

;RECEIVE TOP(1) @.MsgBody = CAST( message_body as XML ),

@.Conversation_Handle_For_Waiting_QB = conversation_handle

FROM WaitingQBs

IF @.Conversation_Handle_For_Waiting_QB is not null

BEGIN

-- end this conversation -- the initiator doesn't care so clean it up

print 'Ending the conversation for the waiting QB'

END CONVERSATION @.Conversation_Handle_For_Waiting_QB

DECLARE @.QBGroupID bigint,@.Who bigint, @.Why varchar(256), @.Retries int

SET @.QBGroupID = @.MsgBody.value('(/createletter/info/@.QBGroupID)[1]','bigint');

SET @.Who = @.MsgBody.value('(/createletter/info/@.Who)[1]','bigint');

SET @.Why = @.MsgBody.value('(/createletter/info/@.Why)[1]','nvarchar(256)');

SET @.Retries = @.MsgBody.value('(/createletter/info/@.Retries)[1]','int');

print @.QBGroupID

print @.Who

print @.Why

print @.Retries

-- now submit this QB to processing

-- first check to see if this qbgroupid is already in the queue

select message_body

from ProcessingLetters

WHERE cast(message_body as xml).value('(/createletter/info/@.QBGroupID)[1]','bigint') = @.QBGroupID

IF @.@.ROWCOUNT = 0

BEGIN

-- adding to processing queue

BEGIN TRANSACTION

DECLARE @.Conversation_Handle_To_Start_Processing uniqueidentifier;

-- 300 = 300 seconds = 5 minutes

BEGIN DIALOG CONVERSATION @.Conversation_Handle_To_Start_Processing

FROM SERVICE QBLetterProcessing

TO SERVICE 'QBLetterProcessing'

ON CONTRACT QBLetterCreation

WITH ENCRYPTION=OFF, LIFETIME = 300;

-- Send a message on the conversation

--DECLARE @.message nvarchar(max), @.xmlmsg XML

SET @.message = '<createletter><info QBGroupID="'+cast(@.QBGroupID AS nvarchar(20))+'" Who="'+cast(@.Who AS nvarchar(20))+'" Why="'+@.Why+'" Retries="'+cast(@.Retries as nvarchar(3))+'" Starttime="'+cast(getdate() as varchar(30))+'" /></createletter>' ;

SET @.xmlmsg = CAST(@.message as XML)

;SEND ON CONVERSATION @.Conversation_Handle_To_Start_Processing MESSAGE TYPE ProcessQB(@.xmlmsg)

COMMIT TRANSACTION

print 'conversation started for processingletters on '+cast(@.Conversation_Handle_To_Start_Processing as varchar(50))

--

-- we don't want to end the conversation...it stays open ..

-- it will end it after processing is done...properly

--

END

ELSE

BEGIN

print 'this qb group is already in processing letters'

END

END

ELSE

BEGIN

print 'getting next qb to process...no conversation handle'

END

-- now retrieve from the QBLetterProcess Queue

exec SB_FinalizeQBForLetterGeneration 0,@.Conversation_Handle_To_Start_Processing,null

DROP SERVICE QBWaiting

DROP SERVICE QBLetterProcessing

DROP SERVICE LetterGrouping

DROP QUEUE WaitingQBs

DROP QUEUE ProcessingLetters

DROP QUEUE WaitingToGroup

DROP CONTRACT QBLetterCreation

DROP MESSAGE TYPE SubmitQB

DROP MESSAGE TYPE ProcessQB

DROP MESSAGE TYPE SubmitGrouping

-- Drop stored procedure if it already exists

IF EXISTS (

SELECT *

FROM INFORMATION_SCHEMA.ROUTINES

WHERE SPECIFIC_SCHEMA = N'dbo'

AND SPECIFIC_NAME = N'SB_FinalizeQBForLetterGeneration'

)

DROP PROCEDURE dbo.SB_FinalizeQBForLetterGeneration

GO

|||

Remus,

Did you run the script? Did you get the same results?

|||

Remus,

So what I'm seeing is that my @.Conversation_Handle_To_Start_Processing is not what is actually stored in the queue as the conversation_handle. So how can I point back to conversation?

CREATE MESSAGE TYPE ProcessQB VALIDATION = WELL_FORMED_XML;

CREATE CONTRACT QBLetterCreation (ProcessQB SENT BY INITIATOR)

CREATE QUEUE ProcessingLetters WITH STATUS=ON

CREATE SERVICE QBLetterProcessing ON QUEUE ProcessingLetters (QBLetterCreation)

-- adding to processing queue

BEGIN TRANSACTION

DECLARE @.Conversation_Handle_To_Start_Processing uniqueidentifier;

-- 300 = 300 seconds = 5 minutes

BEGIN DIALOG CONVERSATION @.Conversation_Handle_To_Start_Processing

FROM SERVICE QBLetterProcessing

TO SERVICE 'QBLetterProcessing'

ON CONTRACT QBLetterCreation

WITH ENCRYPTION=OFF, LIFETIME = 300;

-- Send a message on the conversation

DECLARE @.message nvarchar(max), @.xmlmsg XML

SET @.message = '<createletter><info QBGroupID="11111" Who="111" Why="test" Retries="0" /></createletter>' ;

SET @.xmlmsg = CAST(@.message as XML)

;SEND ON CONVERSATION @.Conversation_Handle_To_Start_Processing MESSAGE TYPE ProcessQB(@.xmlmsg)

COMMIT TRANSACTION

print 'conversation started for processingletters on '+cast(@.Conversation_Handle_To_Start_Processing as varchar(50))

--

--

DECLARE @.MsgBody2 nvarchar(max)

;RECEIVE TOP(1) @.MsgBody2 = CAST(message_body as nvarchar(max))

FROM ProcessingLetters

WHERE conversation_handle = @.Conversation_Handle_To_Start_Processing

print 'receiving letter from in processing'

if @.MsgBody2 is null

BEGIN

DECLARE @.c uniqueidentifier

print 'conversation handle was not found - '+cast(@.Conversation_Handle_To_Start_Processing as varchar(50))

select @.c = conversation_handle from ProcessingLetters

print 'in table: '+cast(@.c as varchar(200))

select * from ProcessingLetters

END

ELSE

BEGIN

print 'conversation handle FOUND!!!! '

print @.MsgBody2

END

DROP SERVICE QBLetterProcessing

DROP QUEUE ProcessingLetters

DROP CONTRACT QBLetterCreation

DROP MESSAGE TYPE ProcessQB

|||

I can't believe that you BEGIN a conversation by giving it a conversation handle and that is not what is stored in the queue(table) as the conversation_handle!!!! Why the heck then do you have to specifiy a conversation handle if it is never going to be used? And if you ARE going to do it that way, then have the SEND tell us what it created as a conversation handle!

So this is what I had to do.

SELECT @.Conversation_Handle_To_Start_Processing = conversation_handle

FROM ProcessingLetters

WHERE cast(message_body as xml).value('(/createletter/info/@.QBGroupID)[1]','bigint') = '11111'

Then I could do my RECEIVE. You have to make sure you have a unique identifier in your message body if you want to do this. Otherwise you have no way of knowing what the conversation handle is.

I think the documentation should say that the conversation handle you send in is not the actual conversation handle that is stored.

|||

A conversation consists from two endpoints, initiator and target, each with its own handle. The initiator endpoint is the one returned by the BEGIN DIALOG. The target endpoint is created by the first message arriving at the target service. Looking into sys.conversation_endpoints will show these two endpoints. The two endpoints belongig to the same conversation will have same value for conversation_id. BOL describes this here: http://msdn2.microsoft.com/en-us/library/ms166083.aspx

In your example, you are sending on the initiator's handle and trying to receive on the same handle. The initiator has no messages to availabe to be received, the message on the queue belongs to the target (since it was sent by initiator to target). You would have to receive this message (using the target's handle) and send back a reply to the initiator. Then the initiator would have a message available to be received.

HTH,
~ Remus

No comments:

Post a Comment