Delete messages in service broker queue - sql-server

I'd like to clear my queue in SQL Server Management Studio, but I don't want to delete the whole queue just the content in the queue (the messages).

Just combining the two previous answers (by Ben and Jānis) for clarity. This worked for me:
declare #c uniqueidentifier
while(1=1)
begin
select top 1 #c = conversation_handle from dbo.queuename
if (##ROWCOUNT = 0)
break
end conversation #c with cleanup
end

Something like this should work:
while(1=1)
begin
waitfor (
receive top(1)
conversation_group_id
from dbo.yourQueue
), timeout 1000;
if (##rowcount = 0)
break;
end

I would use end conversation (that will also remove all related messages from all queues) using statement:
End Conversation #c With CleanUp
if you just receive message, then you leave conversation open.
End Conversation With CleanUp is for specific situations only.

If you are using SQL Server (starting with 2008) you can use RECEIVE
WHILE (0=0)
BEGIN
RECEIVE * FROM dbo.YourQueue;
IF (##ROWCOUNT = 0) BREAK;
END

I had the same issue as the original poster, but I was needing to clear queues with
millions of messages (failed message queues, especially in non production environments that had not been checked for years).
The solution above would have worked, but was processing at less than 10 messages per minute. By doing it in batches I was getting 30000 messages per minute.
The only noteworthy item in the code is where validation = 'N'. This limits the conversation handles to the real messages. There is a duplicate conversation handle for the response/error which gets removed by the end conversation. Without this clause the script would still work, but generate a lot of errors in the output.
declare #conversationBatch table (convH uniqueidentifier)
declare #conversationHandle uniqueidentifier
declare convCursor cursor for
select convH from #conversationBatch
insert into #conversationBatch
select top 1000 conversation_handle
from dbo.queuename WITH (NOLOCK)
where validation = 'N'
while ##rowcount > 0
begin
open convCursor
fetch next from convCursor into #conversationHandle
while ##FETCH_STATUS = 0
begin
end conversation #conversationHandle with cleanup
fetch next from convCursor into #conversationHandle
end
close convCursor
delete from #conversationBatch
insert into #conversationBatch
select top 1000 conversation_handle
from dbo.queuename WITH (NOLOCK)
where validation = 'N'
end
deallocate convCursor

while(1=1)
begin
waitfor (
receive top(1)
conversation_group_id
from kartokumaqueue2), timeout 1000;
if(##ROWCOUNT = 0) break;
end

Related

Fixing ACCESS_METHODS_HOBT_VIRTUAL_ROOT and PAGELATCH_SH waits with Service Broker SEND ON CONVERSATION?

We are having issues with our service broker implementation as volume has scaled up. The code predates me, I have some experience with service broker (specifically around Event Notifications), but not a ton.
Currently, we have a SQL Server 2016 instance with 30 threads picking up messages one-at-a-time (I know, I know) from a queue and doing work upon it, but the Stored Procedure that inserts into the queue is slow with a ton of waits, causing backups in the systems that feed it. Specifically, we see a bunch of PAGELATCH_EX/PAGELATCH_SH in the filegroup that holds the service broker queues that tends to be SEND ON CONVERSATION, but also a ton of ACCESS_METHODS_HOBT_VIRTUAL_ROOT, s well as BROKER_TRANSMISSION_TABLE waits on END CONVERSATION.
First Stored Procedure, the one our app calls to deliver messages for processing.:
CREATE PROCEDURE [etl].[catch_or_release_queue_bundle] ( #xml_bundle XML )
AS
SET NOCOUNT ON
BEGIN
--Initiator Service, Target Service and the Contract
BEGIN DIALOG #InitDlgHandle
FROM SERVICE [//Bundle/Raw/SBInitiatorService]
TO SERVICE '//Bundle/Raw/SBTargetService'
ON CONTRACT [//Bundle/Raw/SBContract]
WITH ENCRYPTION=OFF, LIFETIME = 7200;
--Send the Message
SEND ON CONVERSATION #InitDlgHandle
MESSAGE TYPE [//Bundle/Raw/RequestMessage] (#xml_bundle);
END;
The activated stored procedure that acts upon it:
CREATE PROCEDURE [dbo].[TargetActivProc]
AS
set nocount on
DECLARE #RecvReqDlgHandle UNIQUEIDENTIFIER;
DECLARE #RecvReqMsg xml;
DECLARE #RecvReqMsgName sysname;
WHILE (1=1)
BEGIN
BEGIN TRANSACTION;
--changed from a WAITFOR (RECEIVE top(1) )TIMEOUT 5000 to use a waitfor instead
RECEIVE TOP(1)
#RecvReqDlgHandle = conversation_handle,
#RecvReqMsg = message_body,
#RecvReqMsgName = message_type_name
FROM sbTargetQueue
IF (##ROWCOUNT = 0)
BEGIN
ROLLBACK TRANSACTION;
WAITFOR DELAY '00:00:01' --mdb 20221004 22:30 adding this so that it waits 1 second.
BREAK;
END
-- Disabling this portion. This should be the production procedure call
EXEC message_evaluator #RecvReqMsg
IF #RecvReqMsgName =
N'//Bundle/Raw/RequestMessage'
BEGIN
DECLARE #ReplyMsg NVARCHAR(100);
SELECT #ReplyMsg =
N'<ReplyMsg>Message for Initiator service.</ReplyMsg>';
SEND ON CONVERSATION #RecvReqDlgHandle
MESSAGE TYPE
[//Bundle/Raw/ReplyMessage]
(#ReplyMsg);
END
ELSE IF #RecvReqMsgName =
N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'
BEGIN
END CONVERSATION #RecvReqDlgHandle;
END
ELSE IF #RecvReqMsgName =
N'http://schemas.microsoft.com/SQL/ServiceBroker/Error'
BEGIN
END CONVERSATION #RecvReqDlgHandle;
END
COMMIT TRANSACTION;
END
And the activated stored procedure for the second queue. I honestly have no idea what the purpose of this one is. My only idea is that there's a THIRD queue that was used at one point but no longer receives messages, and this is some sort of rollup SP to close all conversations? There's enough with Service Broker that they added it for a reason, I just don't know why.
CREATE PROCEDURE [dbo].[InititatorActivProc]
AS
SET NOCOUNT on
DECLARE #dh UNIQUEIDENTIFIER;
DECLARE #message_type SYSNAME;
DECLARE #message_body NVARCHAR(4000);
WHILE (1=1)
BEGIN
BEGIN TRANSACTION
WAITFOR (
RECEIVE top(1) #dh = [conversation_handle],
#message_type = [message_type_name],
#message_body = CAST([message_body] AS NVARCHAR(4000))
FROM [sbInitiatorQueue]), TIMEOUT 5000;
IF (##ROWCOUNT = 0)
BEGIN
ROLLBACK TRANSACTION;
BREAK;
END
IF #message_type = '//Bundle/Raw/ReplyMessage' --
BEGIN
END CONVERSATION #dh
END
ELSE
IF #message_type = 'http://schemas.microsoft.com/SQL/ServiceBroker/Error'
BEGIN
RAISERROR ('Received error %s from service [Target]', 10, 1, #message_body) WITH LOG;
END CONVERSATION #dh
END
COMMIT TRANSACTION;
END

SQL Service Broker Simulate a Poison Message

I am reading an expert from a google preview book.
shorturl.at/htF38
I want to basically simulate a poision message in a Service Broker Queue.
The author presents this code fragment to simulate 5 rollbacks .
My question is how does this pieces of code simulate 5 rollbacks beats me.
Per me On line 31 if it does not see a message in the queue the rollback happens once
and the break statement breaks the loop no ?
DECLARE #ch UNIQUEIDENTIFIER
DECLARE #messagetypename NVARCHAR(256)
DECLARE #messagebody XML
WHILE (1=1)
BEGIN
BEGIN TRANSACTION
WAITFOR (
RECEIVE TOP (1)
#ch = conversation_handle,
#messagetypename = message_type_name,
#messagebody = CAST(message_body AS XML)
FROM TargetQueue
),
TIMEOUT 60000
IF (##ROWCOUNT = 0)
BEGIN
ROLLBACK TRANSACTION
BREAK **--Line No 31**
END
-- Rolling back the current transaction
PRINT 'Rollback the current transaction - simulating a poison message...'
ROLLBACK TRANSACTION
END
GO
Now, when you place a message into the TargetQueue and execute the service program
shown in Listing ,you'll see that Service Broker deactivates the queue automatically, and
you’ll get an error message

Initiator never activated when acknowledged of EndDialog

I wish to comply with Remus Rusanu's dialog recycling technique in my SSB implementation. I wrote some activation procedure for initiator queue, in order to hook EndDialog message back from target and clean the Dialog table from the closed conversation handle.
Nevertheless, though EndDialog ack properly reaches initiator side, no activation is triggered, so my message handler cannot operate and clean the place.
CREATE PROCEDURE fdwh.ProcessResponse
AS
BEGIN
DECLARE #dlgId UNIQUEIDENTIFIER;
DECLARE #msgTypeName SYSNAME;
DECLARE #msgBody VARBINARY(MAX);
DECLARE #payloadHistoryId INT;
BEGIN TRY
BEGIN TRANSACTION
WAITFOR(
RECEIVE TOP(1)
#dlgId = [conversation_handle],
#msgTypeName = message_type_name,
#msgBody = message_body
FROM [fdwh].[SenderQueue]), TIMEOUT 10;
-- Message is regular end of conversation, terminate it
IF (#msgTypeName = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog')
BEGIN
END CONVERSATION #dlgId;
DELETE FROM DWH_BOARD.dbo.Dialog
WHERE (DbId = DB_ID()) AND
(DialogId = #dlgId);
END
-- Message is error, extracts and logs number and description
IF (#msgTypeName = N'http://schemas.microsoft.com/SQL/ServiceBroker/Error')
BEGIN
[...]
I expect queue activation to be triggered and http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog message to be processed as well, but it's not. Isn't EndDialog ACK a regular message?
Please find below a Profiler trace screenshot that is self explaining: .
Example is pure local (single instance/two DBs).
Thanks,
Update
A few more metrics for failing queue:
`SELECT que.[name], que.is_activation_enabled, que.is_receive_enabled, que.is_poison_message_handling_enabled, que.activation_procedure, que.max_readers, [execute_as] = (SELECT pri.[name] FROM sys.database_principals pri WHERE pri.principal_id = que.execute_as_principal_id) FROM sys.service_queues que WHERE que.[name] = 'SenderQueue';
GO
SELECT conversation_handle, to_service_name, message_type_name, is_conversation_error, is_end_of_dialog, enqueue_time, transmission_status FROM sys.transmission_queue;
GO
SELECT [name], is_broker_enabled, log_reuse_wait_desc FROM sys.databases WHERE database_id = 8;
GO
EXEC sp_spaceused 'fdwh.SenderQueue';
GO
SELECT * FROM sys.dm_broker_activated_tasks WHERE database_id=8;
GO
SELECT [state], last_activated_time, tasks_waiting FROM sys.dm_broker_queue_monitors WHERE database_id = 8;
GO
`
Activation occurs only when new messages arrive.
"When STATUS = ON, the queue starts the stored procedure specified
with PROCEDURE_NAME when the number of procedures currently running is
less than MAX_QUEUE_READERS and when messages arrive on the queue
faster than the stored procedures receive messages."
https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-queue-transact-sql?view=sql-server-2017
The activation procedure is expected to continue consuming messages until the queue is empty, and remains empty for the duration of its WATFOR ... RECEIVE.
Your activation procedure is missing the loop. It's RECEIVING a single message and exiting. So every time a new message arrives a single old message is consumed. This may appear to work for a while, but if you ever have get a backlog of messages, you'll never catch up.

How to prevent multi threaded application to read this same Sql Server record twice

I am working on a system that uses multiple threads to read, process and then update database records. Threads run in parallel and try to pick records by calling Sql Server stored procedure.
They call this stored procedure looking for unprocessed records multiple times per second and sometimes pick this same record up.
I try to prevent this happening this way:
UPDATE dbo.GameData
SET Exported = #Now,
ExportExpires = #Expire,
ExportSession = #ExportSession
OUTPUT Inserted.ID INTO #ExportedIDs
WHERE ID IN ( SELECT TOP(#ArraySize) GD.ID
FROM dbo.GameData GD
WHERE GD.Exported IS NULL
ORDER BY GD.ID ASC)
The idea here is to set a record as exported first using an UPDATE with OUTPUT (remembering record id), so no other thread can pick it up again. When record is set as exported, then I can do some extra calculations and pass the data to the external system hoping that no other thread will pick this same record again in the mean time. Since the UPDATE that has in mind to secure the record first.
Unfortunately it doesn't seem to be working and the application sometimes pick same record twice anyway.
How to prevent it?
Kind regards
Mariusz
I think you should be able to do this atomically using a common table expression. (I'm not 100% certain about this, and I haven't tested, so you'll need to verify that it works for you in your situation.)
;WITH cte AS
(
SELECT TOP(#ArrayCount)
ID, Exported, ExportExpires, ExportSession
FROM dbo.GameData WITH (READPAST)
WHERE Exported IS NULL
ORDER BY ID
)
UPDATE cte
SET Exported = #Now,
ExportExpires = #Expire,
ExportSession = #ExportSession
OUTPUT INSERTED.ID INTO #ExportedIDs
I have a similar set up and I use sp_getapplock. My application runs many threads and they call a stored procedure to get the ID of the element that has to be processed. sp_getapplock guarantees that the same ID would not be chosen by two different threads.
I have a MyTable with a list of IDs that my application checks in an infinite loop using many threads. For each ID there are two datetime columns: LastCheckStarted and LastCheckCompleted. They are used to determine which ID to pick. Stored procedure picks an ID that wasn't checked for the longest period. There is also a hard-coded period of 20 minutes - the same ID can't be checked more often than every 20 minutes.
CREATE PROCEDURE [dbo].[GetNextIDToCheck]
-- Add the parameters for the stored procedure here
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN TRANSACTION;
BEGIN TRY
DECLARE #VarID int = NULL;
DECLARE #VarLockResult int;
EXEC #VarLockResult = sp_getapplock
#Resource = 'SomeUniqueName_app_lock',
#LockMode = 'Exclusive',
#LockOwner = 'Transaction',
#LockTimeout = 60000,
#DbPrincipal = 'public';
IF #VarLockResult >= 0
BEGIN
-- Acquired the lock
-- Find ID that wasn't checked for the longest period
SELECT TOP 1
#VarID = ID
FROM
dbo.MyTable
WHERE
LastCheckStarted <= LastCheckCompleted
-- this ID is not being checked right now
AND LastCheckCompleted < DATEADD(minute, -20, GETDATE())
-- last check was done more than 20 minutes ago
ORDER BY LastCheckCompleted;
-- Start checking
UPDATE dbo.MyTable
SET LastCheckStarted = GETDATE()
WHERE ID = #VarID;
-- There is no need to explicitly verify if we found anything.
-- If #VarID is null, no rows will be updated
END;
-- Return found ID, or no rows if nothing was found,
-- or failed to acquire the lock
SELECT
#VarID AS ID
WHERE
#VarID IS NOT NULL
;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH;
END
The second procedure is called by an application when it finishes checking the found ID.
CREATE PROCEDURE [dbo].[SetCheckComplete]
-- Add the parameters for the stored procedure here
#ParamID int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN TRANSACTION;
BEGIN TRY
DECLARE #VarLockResult int;
EXEC #VarLockResult = sp_getapplock
#Resource = 'SomeUniqueName_app_lock',
#LockMode = 'Exclusive',
#LockOwner = 'Transaction',
#LockTimeout = 60000,
#DbPrincipal = 'public';
IF #VarLockResult >= 0
BEGIN
-- Acquired the lock
-- Completed checking the given ID
UPDATE dbo.MyTable
SET LastCheckCompleted = GETDATE()
WHERE ID = #ParamID;
END;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH;
END
It does not work because multiple transactions might first execute the IN clause and find the same set of rows, then update multiple times and overwrite each other.
LukeH's answer is best, accept it.
You can also fix it by adding AND Exported IS NULL to cancel double updates.
Or, make this SERIALIZABLE. This will lead to some blocking and deadlocking. This can safely be handled by timeouts and retry in case of deadlock. SERIALIZABLE is always safe for all workloads but it might block/deadlock more often.

Service Broker : Sys.Conversation_endpoints filling up with CO/CONVERSING messages when using With Cleanup

We recently identified a problem with one of our databases where as a result of a 'fire & forget' setup (i.e: conversations being closed immediately after sending), our sys.conversation_endpoints table was filling up with DI/DISCONNECTED_INBOUND messages. This eventually spilled over into the tempDB, causing it to grow enormously and eat up precious disk space. We eventually resolved this issue by commenting out the line
END CONVERSATION #handle WITH CLEANUP
in our sending SP and closing the conversations in our receiving SP using the same code,
END CONVERSATION #handle WITH CLEANUP
However, we now have a new issue. Since moving servers (and migrating from SQL Server 2005 to SQL Server 2008) we've recently discovered that sys.conversation_endpoints is now filling up with CO/CONVERSING messages, indicating that the conversations are not being closed. The receiving SP is closing them, or at least is running the command to do so, so I don't understand where these messages are coming from.
I've tried going back to ending the conversation at the point of send, but it has no effect. Is it wrong to end conversations on the receiving end using WITH CLEANUP? Or is there some other problem?
This post on techtarget seems to suggest its a bug, and that running a job to cleanup the leftovers is the only solution...
UPDATE:
Pawel pointed out below that I should be avoiding the Fire & Forget Pattern, and I've added an activated SP to the initiator queue to end any conversations. However, sys.conversation_endpoints is STILL filling up, this time with CD/CLOSED messages. Here's the structure of my queues
Send_SP:
DECLARE #h UNIQUEIDENTIFIER
BEGIN DIALOG CONVERSATION #h
FROM SERVICE 'InitiatorQueue' TO SERVICE 'TargetQueue'
ON CONTRACT 'MyContract' WITH ENCRYPTION = OFF;
SEND ON CONVERSATION #h MESSAGE TYPE 'MyMessage' (#msg)
Receive_SP (Activated SP on TargetQueue)
DECLARE #type SYSNAME, #h UNIQUEIDENTIFIER, #msg XML;
DECLARE #target TABLE (
[message_type_name] SYSNAME,
[message_body] VARBINARY(MAX),
[conversation_handle] UNIQUEIDENTIFIER
)
WHILE(1=1)
BEGIN TRANSACTION
WAITFOR(RECEIVE TOP (1000)
[message_type_name],[message_body],[conversation_handle]
FROM TargetQueue INTO #target), TIMEOUT 2000
IF(##rowcount!=0)
BEGIN
WHILE((SELECT count(*) FROM #target) > 0)
BEGIN
SELECT TOP (1) #type = [message_type_name],
#msg = [message_body],
#h = [conversation_handle] FROM #target;
// Handle Message Here
END CONVERSATION #h;
DELETE TOP (1) FROM #target;
END
END
COMMIT TRANSACTION;
End_SP (Activated SP on InitiatorQueue)
DECLARE #type SYSNAME, #h UNIQUEIDENTIFIER, #msg XML;
DECLARE #init TABLE (
[message_type_name] SYSNAME,
[message_body] VARBINARY(MAX),
[conversation_handle] UNIQUEIDENTIFIER
)
WHILE(1=1)
BEGIN TRANSACTION
WAITFOR(RECEIVE TOP (1000)
[message_type_name],[message_body],[conversation_handle]
FROM InitiatorQueue INTO #init), TIMEOUT 2000
IF(##rowcount!=0)
BEGIN
WHILE((SELECT count(*) FROM #init) > 0)
BEGIN
SELECT TOP (1) #type = [message_type_name],
#msg = [message_body],
#h = [conversation_handle] FROM #init;
END CONVERSATION #h;
DELETE TOP (1) FROM #init;
END
END
COMMIT TRANSACTION;
Using the fire-and-forget pattern will inevitably lead to this and other types of issues. Additionally it will make any hypothetical errors go unnoticed. Is there any reason why you can't change the message exchange pattern so that the target issues END CONVERSATION (without cleanup!) once it receives a message and then the initiator only calls END CONVERSATION (again, without cleanup) upon receiving end conversation message from the target?

Resources