Showing posts with label broker. Show all posts
Showing posts with label broker. Show all posts

Tuesday, March 20, 2012

Conversations

I am currently designing an auditing application using Service Broker. Right now, when I send a message from a trigger, I start a conversation, and later on when the message has been processed, the conversation has ended. One thing I am concerned with is that when a lot of updates are occurring on the system, if the amount of conversations being created will eat up system resources. Does it make sense to create them and end them later, or should I try to reuse them?
Tim

This subject is quite intricate and has many facets. It also appears often when discussing Service Broker, so I'll try to address it in a series of blog articles. I've started this today, see http://blogs.msdn.com/remusrusanu/archive/2007/04/24/reusing-conversations.aspx

HTH,

~ Remus

|||Thanks Remus. I am starting to notice that I am getting messages such as "There is insufficient system memory to run this query." and "There is insufficient memory available in the buffer pool." when I have a lot of conversations occurring (about 300K records in conversation_endpoints view). I am thinking that this is directly attributable to me creating a new conversation for every audit record(s) created. What is the best way for me to test that this is the case...that Service Broker is really the culprit in tying up all of my system memory?|||look in sys.dm_os_memory_clerks to see how memory is allocated|||Ok, sounds good. I am almost 100% sure it relates to me creating a new dialog for each message I pass.

Do you plan to post another blog anytime soon regarding reusing conversations? The situation I am currently trying to figure out is how to handle closing (or handling) the conversations so that I can reuse them....more specifically:
1. I check a table to see if there are any dialog handles free to use. If they are not, I create a new one and send a message to a queue.
2. The activation proc on the queue gets the message from the queue, but the handle it receives is not the same as the one that was created when I sent the message. It seems that this handle represents the target (from sys.conversation_endpoints). At this point, I can't close that end of the conversation when I have processed the message because if I do, it puts the other end, the initiator, in a disconnected_inbound state, which means I can't reuse it later and send another conversation on it. So, what is the best way to handle that? I want to be able to reuse the handle that I originally created, but not really sure the best way to do it. Thanks in advance.
Tim|||

Yes, I plan a post soon. Here is how I recommend doing it: have a criteria when a dialog should be 'recycled' (ended and a new one started). Good candidate criterias would be 'after N messages sent' or 'X minutes/hours/days after was created'. When this criteria is met, the initiator should sent a special message, something like 'EndOfStream' and removes the handle from the association table (So subsequent usp)Send calls will start a new one). When the target receives this EndOfStream message, it responds with and END CONVERSATION. When the initiator receives the EndDialog message, it ends it side (initiator also must have activation on it's queue). I have arguments why I prefer this pattern, I'll detail in blog.

HTH,

~ Remus

|||Thanks Remus, I eagerly look forward to it. Also, here is a small dump of my dm_os_memory_clerks view when I was receiving the errors: type single_pages_kb multi_pages_kb OBJECTSTORE_SERVICE_BROKER 884584 0 CACHESTORE_BROKERTO 176936 0 MEMORYCLERK_BHF 146696 0 OBJECTSTORE_LOCK_MANAGER 126064 0 OBJECTSTORE_SERVICE_BROKER 101168 0 MEMORYCLERK_SQLSERVICEBROKER 19256 192 MEMORYCLERK_SQLSTORENG 10624 7088 CACHESTORE_OBJCP 6304 32 MEMORYCLERK_SOSNODE 6224 6048 MEMORYCLERK_SQLGENERAL 1832 2016 I also started getting a fun new error in one of my activation procedures: Internal Error: Text manager cannot continue with current statement. Run DBCC CHECKTABLE., which I think is directly related to me creating a new dialog for every message created.|||

This is a procedure I wrote to manage a dialog pool. Basically it creates a number of conversations and then uses them until the number available drops below a certain threshold value. It then selects one at random (so you aren't reusing the same one every time). It works great, but I'd like to hear any comments from the experts.

CREATE PROCEDURE [usp_DialogFactoryCreate]

(

@.minDialogs AS INT,

@.maxDialogs AS INT,

@.fromServiceName AS NVARCHAR(256),

@.toServiceName AS NVARCHAR(256),

@.contractName AS NVARCHAR(256),

@.selectedDialog UNIQUEIDENTIFIER OUTPUT

)

AS

BEGIN

SET NOCOUNT ON;

DECLARE @.dialogCount INT;

DECLARE @.conversationHandle AS UNIQUEIDENTIFIER;

-- State should be either STARTED_OUTBOUND or CONVERSING

SET @.dialogCount = (SELECT COUNT(*) FROM sys.conversation_endpoints WITH (NOLOCK)

WHERE far_service = @.toServiceName

AND state IN ('SO', 'CO'));

-- Create dialogs until we hit the maximum

-- This will also dictate how many activated procedures will be created for the queue

IF ( @.dialogCount < @.minDialogs)

BEGIN

WHILE (@.dialogCount <= @.maxDialogs)

BEGIN

-- Create dialogs with infinite lifetime for our pool

BEGIN DIALOG CONVERSATION @.conversationHandle

FROM SERVICE @.fromServiceName

TO SERVICE @.toServiceName

ON CONTRACT @.contractName

WITH ENCRYPTION = OFF;

SET @.dialogCount = @.dialogCount + 1;

END

END

-- Randomly select a dialog conversation

SET @.selectedDialog = (SELECT TOP(1) conversation_handle

FROM sys.conversation_endpoints

WHERE far_service = @.toServiceName AND state IN ('SO', 'CO')

ORDER BY NEWID());

RETURN (0);

END

GO

|||Variuos threads/transaction calling this procedure will conflict for the same conversation and cause contention.|||

Hi Remus,

Ive solved my memory problem by reusing dialogs based upon how long they have been in use. However, now I am running into another tricky problem. What I am noticing when many messages are being passed around is that internal service broker tables are causing a huge number of locks in the database, sometimes over 100,000 of them, which will really lock up other processes on the server. How are these internal tables (QUEUE_MESSAGES_) constructed? Is it a matter of one per message received and processed? I have a feeling that it is being caused by me receiving (RECEIVE TOP(1)) one message at a time and processing that way. I know it isn't a great way to do it, and it is slower, but is it what is causing all of these internal locking in the database? BTW...reusing a dialog based upon how long it has been open was a great idea...thank you very much for it.

Tim

Conversation ID cannot be associated with an active conversation

Hi:

My service broker was working perfectly fine earlier. As I was testing...I recreated the whole service broker once again.

Now I am able to get the message at the server end from intiator. When trying to send message from my server to the intiator it gives this error in sql profiler.

broker:message undeliverable: This message could not be delivered because the Conversation ID cannot be associated with an active conversation. The message origin is: 'Transport'.

broker:message undeliverable This message could not be delivered because the 'receive sequenced message' action cannot be performed in the 'ERROR' state.

How do I proceed now ?

Thanks,

Pramod

This is happening randomly....

Now When I am sending the message I am getting this error in intiator sql profiler.

broker:message undeliverable: This message could not be delivered because the Conversation ID cannot be associated with an active conversation. The message origin is: 'Transport'....

What does this mean ?

Thanks,

Pramod

|||Did you backup, move and restore the initiator database? The error you are seeing could be produced because the initiator endpoint and the target endpoint are not in sync which could be the result of a backup/restore operation. If it is possible, can you drop all services and start all over on the two instances?|||I meant "did you backup, move and restore the TARGET database"|||

In fact I created new databases, new services, new endpoints on both sides i.e on both instances.

Pramod

|||

The problem is from the END CONVERSATION ... WITH CLEANUP. Don't use it, use simple END CONVERSATION. See this http://blogs.msdn.com/remusrusanu/archive/2006/01/27/518455.aspx

HTH,
~ Remus

|||

Remus:

I removed with cleanup in my sprocs...I notice other interesting things happening.

The error still comes up in the SQL profiler, but the message is delivered randomly.If I try sending 3 times 1 time it reachs target service broker.

One more interesting thing is my xml message which I sent is garbled in the target. Its not the way I sent to target from initiator.

Thanks,

Pramod

|||

Pramod S Kumar wrote:


...the message is delivered randomly.If I try sending 3 times 1 time it reachs target service broker.

Typically this means that there are more instances of the target service and Service Broker does a load balancing across them. Make sure you don't have the same target service in another database you forgot about. Alternatively you can specify the desired broker instance in the BEGIN DIALOG to force the selected target service.

Also, see this post here http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=335683&SiteID=1

Pramod S Kumar wrote:


One more interesting thing is my xml message which I sent is garbled in the target. Its not the way I sent to target from initiator.

Can you give an example of how the payload is garbled?
Please note that Unicode XML has a Byte Order Mark (BOM) like 0xFFFE in front of the XML stream. Also, make sure you don't mix VARCHAR and NVARCHAR types when sending/receiving the message. The best practice is to always use the XML datatype for this. If the SEND payload is declared in the T-SQL batch, declare it as XML. If is a parameter sent from Ado.NET, use the System.Data.SqlDbType.Xml parameter type. Same applies to receiving the message, assign the message_body to a XML type.

HTH,
~ Remus

|||

Remus:

Prblm 1:
--
I am forcing to target service name here...Hence that should not be problem.

DECLARE @.dialog_handle uniqueidentifier,

@.msg XML

BEGIN DIALOG CONVERSATION @.dialog_handle

FROM SERVICE CLIENTSERVICE

TO SERVICE 'SERVERSERVICE'

ON CONTRACT MainContract

WITH ENCRYPTION = OFF ;

Prblm 2:
--
This works fine b/w 2 instances in local server but doesnt work b/w 2 different servers.

Here is my table structure for both target and initiator.

CREATE TABLE [dbo].[messages_log](
[logid] [int] IDENTITY(1,1) NOT NULL,
[logdata] [varchar](max) COLLATE Latin1_General_CI_AI NULL,
[msgdata] [xml] NULL,
CONSTRAINT [PK_messages_log] PRIMARY KEY CLUSTERED
(
[logid] ASC
) ON [PRIMARY]
) ON [PRIMARY]

GO

Thanks,

Pramod

|||

Pramod S Kumar wrote:

Remus:

Prblm 1:
--
I am forcing to target service name here...Hence that should not be problem.

DECLARE @.dialog_handle uniqueidentifier,

@.msg XML

BEGIN DIALOG CONVERSATION @.dialog_handle

FROM SERVICE CLIENTSERVICE

TO SERVICE 'SERVERSERVICE'

ON CONTRACT MainContract

WITH ENCRYPTION = OFF ;

I think you missed my point. Unless you specify a broker instance, the load balancing is probably the problem. Your script does not specify a broker instance.

Pramod S Kumar wrote:

Prblm 2:
--
This works fine b/w 2 instances in local server but doesnt work b/w 2 different servers.

Here is my table structure for both target and initiator.

CREATE TABLE [dbo].[messages_log](
[logid] [int] IDENTITY(1,1) NOT NULL,
[logdata] [varchar](max) COLLATE Latin1_General_CI_AI NULL,
[msgdata] [xml] NULL,
CONSTRAINT [PK_messages_log] PRIMARY KEY CLUSTERED
(
[logid] ASC
) ON [PRIMARY]
) ON [PRIMARY]

GO

This doesn't help me in any way. I'm asking you to show me an example of how the actual XML message is different between the one you SEND and the one you RECEIVE.

|||

Remus:

Ok you meant to specify broker instance while specifying route...if that is the case here is the script..
Initiator:
CREATE ROUTE SERVERROUTE

WITH

BROKER_INSTANCE = '3F070C35-3C1E-4FA7-B654-33280DA1482B',

SERVICE_NAME = 'SERVERSERVICE' ,

ADDRESS = 'tcp://10.23.2.145:6099';

Target:
CREATE ROUTE CLIENTROUTE

WITH

BROKER_INSTANCE = '4FB2019E-D9D0-4665-9FF6-262D5C33A3D5',

SERVICE_NAME = 'CLIENTSERVICE' ,

ADDRESS = 'tcp://10.23.2.146:6022';

GO

Ok with xml....here is the example...

Original XML

<queue userid="23" Friendlyname="more download" TemplateName="TempDownloadReportName">

<filters columnkey="VDATE8" datatype="0">

<criteria leftarg="3/26/2005 12:00:00 AM" logop="0" rightarg="4/1/2005 12:00:00 AM">

<fields field="YRMTH" datatype="2" grouporder="-1" summed="0" averaged="0" counted="1" />

<fields field="SLINE" datatype="2" grouporder="1" summed="0" averaged="0" counted="0" />

<fields field="VESSEL" datatype="2" grouporder="2" summed="0" averaged="0" counted="0" />

<fields field="COMMODITY" datatype="2" grouporder="3" summed="0" averaged="0" counted="0" />

<fields field="REEFER" datatype="3" grouporder="4" summed="0" averaged="0" counted="0" />

</criteria>

</filters> </queue>

XML received at target:

<queue userid="23" Friendlyname="more download" TemplateName="TempDownloadReportName">

<filters columnkey="VDATE8" datatype="0">

<criteria leftarg="3/26/2005 12:00:00 AM" logop="0" rightarg="4/1/2005 12:00:00 AM">

<fields field="YRMTH" datatype="2" grouporder="-1" summed="0" averaged="0" counted="1" />

</criteria>

</filters>
<filters columnkey="VDATE8" datatype="0">

<criteria leftarg="3/26/2005 12:00:00 AM" logop="0" rightarg="4/1/2005 12:00:00 AM">

<fields field="SLINE" datatype="2" grouporder="1" summed="0" averaged="0" counted="0" />

</criteria>

</filters>
<filters columnkey="VDATE8" datatype="0">

<criteria leftarg="3/26/2005 12:00:00 AM" logop="0" rightarg="4/1/2005 12:00:00 AM">

<fields field="VESSEL" datatype="2" grouporder="2" summed="0" averaged="0" counted="0" />
</criteria>

</filters>
<filters columnkey="VDATE8" datatype="0">

<criteria leftarg="3/26/2005 12:00:00 AM" logop="0" rightarg="4/1/2005 12:00:00 AM">

<fields field="COMMODITY" datatype="2" grouporder="3" summed="0" averaged="0" counted="0" />

</criteria>

</filters>
<filters columnkey="VDATE8" datatype="0">

<criteria leftarg="3/26/2005 12:00:00 AM" logop="0" rightarg="4/1/2005 12:00:00 AM">

<fields field="REEFER" datatype="3" grouporder="4" summed="0" averaged="0" counted="0" />

</criteria>

</filters>
</queue>

Now...when I tried today...I am not able to send any messages from target to initiator.I have also enabled message forwarding.

Thanks,

Pramod

|||

Pramod S Kumar wrote:

Remus:

Ok you meant to specify broker instance while specifying route...

Sorry about the confusion. I actually meant specifying the broker instance in the BEGIN DIALOG statement, like this:

BEGIN DIALOG CONVERSATION @.dialog_handle

FROM SERVICE CLIENTSERVICE

TO SERVICE 'SERVERSERVICE', '3F070C35-3C1E-4FA7-B654-33280DA1482B'

ON CONTRACT MainContract

WITH ENCRYPTION = OFF ;

Pramod S Kumar wrote:

Ok with xml....here is the example...

Original XML

<queue userid="23" Friendlyname="more download" TemplateName="TempDownloadReportName">

<filters columnkey="VDATE8" datatype="0">

<criteria leftarg="3/26/2005 12:00:00 AM" logop="0" rightarg="4/1/2005 12:00:00 AM">

<fields field="YRMTH" datatype="2" grouporder="-1" summed="0" averaged="0" counted="1" />

<fields field="SLINE" datatype="2" grouporder="1" summed="0" averaged="0" counted="0" />

<fields field="VESSEL" datatype="2" grouporder="2" summed="0" averaged="0" counted="0" />

<fields field="COMMODITY" datatype="2" grouporder="3" summed="0" averaged="0" counted="0" />

<fields field="REEFER" datatype="3" grouporder="4" summed="0" averaged="0" counted="0" />

</criteria>

</filters> </queue>

XML received at target:

<queue userid="23" Friendlyname="more download" TemplateName="TempDownloadReportName">

<filters columnkey="VDATE8" datatype="0">

<criteria leftarg="3/26/2005 12:00:00 AM" logop="0" rightarg="4/1/2005 12:00:00 AM">

<fields field="YRMTH" datatype="2" grouporder="-1" summed="0" averaged="0" counted="1" />

</criteria>

</filters>
<filters columnkey="VDATE8" datatype="0">

<criteria leftarg="3/26/2005 12:00:00 AM" logop="0" rightarg="4/1/2005 12:00:00 AM">

<fields field="SLINE" datatype="2" grouporder="1" summed="0" averaged="0" counted="0" />

</criteria>

</filters>
<filters columnkey="VDATE8" datatype="0">

<criteria leftarg="3/26/2005 12:00:00 AM" logop="0" rightarg="4/1/2005 12:00:00 AM">

<fields field="VESSEL" datatype="2" grouporder="2" summed="0" averaged="0" counted="0" />
</criteria>

</filters>
<filters columnkey="VDATE8" datatype="0">

<criteria leftarg="3/26/2005 12:00:00 AM" logop="0" rightarg="4/1/2005 12:00:00 AM">

<fields field="COMMODITY" datatype="2" grouporder="3" summed="0" averaged="0" counted="0" />

</criteria>

</filters>
<filters columnkey="VDATE8" datatype="0">

<criteria leftarg="3/26/2005 12:00:00 AM" logop="0" rightarg="4/1/2005 12:00:00 AM">

<fields field="REEFER" datatype="3" grouporder="4" summed="0" averaged="0" counted="0" />

</criteria>

</filters>
</queue>

These are not differences from Service Broker, but either from your processing or from the XML column storage in the table. If you would compare the XML in the queue itself (the one returned by RECEIVE), you'd see it identically with the one sent.

Note that XML data is not a string, you may have different representations of the same XML fragment that are equivalent.

|||

Remus:

I did make all the changes u specified.

Today I am not able to send any message when I send a message...I still have same error...in SQL Profiler.

This message could not be delivered because the Conversation ID cannot be associated with an active conversation. The message origin is: 'Transport'.

Thanks,

Pramod

|||

This means that you still have a conversation that is sending messages to it's peer conversation endpoint that was ended WITH CLEANUP.

Cleanup all databases involved (use ALTER DATABASE ... SET NEW_BROKER) and make sure that there are no more END CONVERSATION ... WITH CLEANUP in your scripts.

HTH,
~ Remus

Conversation Groups

I am thinking of updating my SQL monitoring application to use Service Broker.

Right now I loop through my list of servers performing various checks on each server. Things like 'check last database backup', 'check for new databases', 'check for server restart'. I loop through, one server at a time, doing one check at a time. The more servers I have the longer it is taking.

So, I want to multi-thread the servers, but single-thread the checks on each individual server. This way I can check say, 5 servers at a time, but on each server I will only do one check at a time. This way I won't flood an individual server with multiple checks.

Is this possible? It looks like Conversation groups might be the way to go but I'm not sure.

Conversatiuon groups are always local, as in no conversation group info ever goes across the wire with a message. The purpose of conversation groups is to lock together logically related conversations.

To have each server do only one check at a time, is eanough to restrict the activated max count to 1. This way there's at most one instance of the activated procedure doing one check. Multiple requests can be sent to the same server, they'll simply be queued up and wait their turn.

The problem you describe can also be aproached as a pub-sub problem (see https://blogs.msdn.com/remusrusanu/archive/2005/12/12/502942.aspx). The publisher (your central administrative service) publishes 'check requests'. All instances you monitor are subscribed to this publisher and receive the request, perform the check and report the result.

HTH,
~ Remus

|||

Thanks for the reply.

I don't think I gave you enough info. All my monitoring processes are running on the same server. I connect out to the 'monitored' servers using linked servers. I can't have the queues on the 'monitored' server as most of them are SQL 2000.

The whole monitoring is run from one SQL 2005 server.

I will take a look at the article you mentioned.

Thanks

Conversation Group Question

I'm trying to use Service Broker to relate a set of messages together and was trying to use a related conversation group id. From what I can gather (looking at other threads) I can't use this.....

Basically, my ideas was.... I have several tables being updated within a database transaction. These tables will have triggers associated with them which send a message to a SB queue detailing the table that has been affected and the key information.

After the database transaction commits, I wanted to retrieve the group of messages in order to identify exactly what happened to the database during the transaction (for business reasons). I don't need necessarily need them in the same order, but do need them grouped by database transaction.

Service Broker seemed to be ideal i.e. the messages wouldn't commit if the database transaction rolled back and I wouldn't be able to access them until the entire transaction was committed........ My only problem is that I don't seem to be able to associate them with each other!!!!

Can anyone help with a way I can do this with Service Broker, or am I just trying to use the wrong technology?

If you can avoid doing the SEND in the trigger but instead do it in the batch executing the updates, then you could use a single dialog for sending all your messages as shown below:

DECLARE @.dh UNIQUEIDENTIFIER;
BEGIN TRANSACTION;
BEGIN DIALOG @.dh ...;
UPDATE TABLE t0 ...;
SEND ON CONVERSATION @.dh ...;
UPDATE TABLE t1 ...;
SEND ON CONVERSATION @.dh...;
...
COMMIT;

If you must do the SEND in the trigger, then you will have to somehow pass the conversation handle of an opened dialog or a conversation group id to bind new dialogs to from your batch to the trigger. I suppose that will involve some sort of temporary table.

Conversation enpoints are not getting cleaned up on target end

Hi,

We are using service broker between two different instances. But were facing issues with increasing row count in conversation_endpoints view. We found that this was because we were using default value for lifetime for the conversation which is value of size int. Later on we changed the lifetime to 1 minute and conversation_endpoints view start getting cleaned up after 30 minutes

Following commands are used to send message

Before :

BEGIN DIALOG CONVERSATION @.handle
FROM SERVICE @.SendService
TO SERVICE @.ReceiveService
ON CONTRACT @.Contract
SEND ON CONVERSATION @.handle
MESSAGE TYPE @.xmlMessageType(@.xmlMessage);

END CONVERSATION @.handle;

After:

BEGIN DIALOG CONVERSATION @.handle
FROM SERVICE @.SendService
TO SERVICE @.ReceiveService
ON CONTRACT @.Contract
WITH LIFETIME = @.lifetime;

SEND ON CONVERSATION @.handle
MESSAGE TYPE @.xmlMessageType(@.xmlMessage);


END CONVERSATION @.handle;

But as we use default life time for a long due to which around 15 million records got acumlated in this view. What is the best way to clean up this view.


END Conversation @.handle with cleanup is taking so long is their any other way to do this

Thanks,

Prashant

Not sure if this is the total cause of your problem but ending a conversation before the first message is sent on it tends to leave it in an unstable state. Remus has a good explanation here: http://blogs.msdn.com/remusrusanu/archive/2006/04/06/570578.aspx|||

Thanks for information. But as of now i want to know what is the best way do cleanup on sys.conversation_endpoints with 14 million rows

|||

I see - not interested in doing it right but doing it wrong faster.

I assume you have a script that loads all the dialog handles into a cursor and then calls END DIAlOG WITH CLEANUP on each one. If you want your application to continue working while you are cleaning up then that's the only way. If you can shut down your applicationso that there are no dialogs or messages that you care about then an ALTER DATABASE command with the SET NEW_BROKER parameter will blow away all traces of any dialogs and messages.

|||

If you moved to SP2, you can do ALTER DATABASE ... SET NEW_BROKER, it will truncate every relevant internal table (conversation_endpoints, conversation_groups, transmission_queue and all message queues).

Do not attempt this with pre-SP2 SQL 20005 because it will literaly do 14 mil END CONVERSATION ... WITH CLEANUP in one transaction lasting forever.

Note that NEW_BROKER will nuke every conversation, including currently active ones. If you cannot afford this, then you must END conversations individually, if you batch commit it doesn't take that long actually.

|||

Thanks for the instant replies. Unfortunately we cannot shutdown the application here. So i think only option left is using WITH CLEANUP. But this might be a help in sometime in future. Thanks again for wonderful upport

|||In that case, be sure to use Remus' suggestion of batch commits. END a few hundred conversations and then commit the transaction. This is much more efficient than doing each END CONVERSATION in its own transaction.