Introduction
SQL Server Service Broker (SSBS) is a new architecture (introduced with SQL Server 2005 and enhanced further in SQL Server 2008 and SQL Server 2008 R2) which allows you to write asynchronous, decoupled, distributed, persistent, reliable, scalable and secure queuing/message based applications within the database engine itself.
In my previous couple of articles, I introduced you SQL Server Service Broker, what it is, how it works, what its different components are and how they are related to each other. Then I talked about writing SSBS applications in three different scenarios:
· Initiator and Target in same database
· Initiator in one database and Target in another database on same instance
· Initiator and Target in separate databases on separate SQL Server instances
In all these examples we manually send and retrieve the messages from the queue and process it. Do we really need to manually retrieve and process the message every time it arrives? Do we have to create a queue monitoring system to monitor for incoming messages on the queue? The simple answer is, NO.
SQL Server Service Broker allows you to create activation stored procedures, which are called automatically (by Service Broker – Queue Monitor) whenever a message arrives in the queue (very much like a trigger but not exactly the same). Not only this, you can specify the number of instances of the stored procedure to be created to process messages from different conversation groups in parallel if the queue is overloaded with the arrival of lots of messages.
SQL Server Service Broker allows you to set up two types of activation, Internal Activation or External Activation. To handle increased message traffic in internal activation you specify a stored procedure (multiple instances of this stored procedure might be created depending on your setting) to be called allowing you to scale up your distributed application. In the case of external activation, Service Broker sends notification (QUEUE_ACTIVATION event) to an external application/program outside of SQL Server to read the message from the queue and process it. This allows you to actually scale out your distributed application. External activation puts processing logic from outside SQL Server into a separate process, which gives better performance and scalability.
In this article, I will be talking about setting up internal activation and writing a stored procedure, which will be called on activation. In the next article, I will be talking about external activation in detail.
Specifying the Internal Activation
When we create a queue, we can specify the activation settings for the queue. The general syntax of creating the queue is given below:
CREATE QUEUE <Queue_name>
[ WITH
[ STATUS = { ON | OFF } [ , ] ]
[ RETENTION = { ON | OFF } [ , ] ]
[ ACTIVATION (
[ STATUS = { ON | OFF } , ]
PROCEDURE_NAME = <Activation_stored_procedure> ,
MAX_QUEUE_READERS = #No_Of_max_readers_Of_Queue ,
EXECUTE AS { SELF | ‘user_name’ | OWNER }
) ]
[ POISON_MESSAGE_HANDLING (
[ STATUS = { ON | OFF } , ])
]
[ ON { <filegroup> | [ DEFAULT ] } ]
Activation removes the overhead of the processing module to keep on polling the queue for incoming messages. It allows you to create a stored procedure and make it an activator (with PROCEDURE_NAME clause), which is activated when a message arrives in the queue, to process it. Service Broker might create another instance of a stored procedure automatically to process the message the moment it arrives in the queue, for detail see the MSDN article, Understanding When Activation Occurs. You can even specify the number of simultaneous instances of stored procedures (with MAX_QUEUE_READERS clause) that can be created by Service Broker to process messages of different conversation groups allowing you to achieve parallel processing. Please note, messages of a particular conversation group are processed by only one instance of the stored procedure, which means parallelism is available across conversation groups and not across messages within a conversation group.
The EXECUTE AS clause is used to specify the user account credential under which the activation stored procedure runs. If it is OWNER it means the activation procedure will run under the credential of the owner of the queue whereas SELF means the activation procedure will run under the credential of current user or the user executing the CREATE QUEUE statement. You can also specify a different user_name under whose credentials you want the activation procedure to run. To learn more about EXECUTE AS see Context Switching or Database Impersonation using EXEC AS.
By default Service broker handles poison messages (POISON_MESSAGE_HANDLING), which means it disables the queue when some offending messages cause a transaction to rollback five times. Starting with SQL Server 2008 R2 you can disable this default behavior if you have setup custom poison message handling mechanism in place.
In the example below, I am creating a target queue and specifying an activation procedure (Prc_RequestQueueProcessor), with a maximum 10 instances of this stored procedure to be created for parallel processing messages from 10 different conversation groups. Next I am disabling default poison message handling and hence no queue will be disabled even if the transaction rollbacks for more than five times. The activation procedure retrieves the message from the target queue, processes it and sends the response back on the same dialog handle and ends the conversation from the target side.
Creating Queue with activation stored procedure |
USE SSBSTargetDB; GO –Create a queue which is an internal physical table to hold –the messages passed to the service, by default it will be –created in default file group, if you want to create it in –another file group you need to specify the ON clause with –this statement. You can use SELECT statement to query this –queue or special table but you can not use other DML statement –like INSERT, UPDATE and DELETE. You need to use SEND and RECEIVE –commands to send messages to queue and receive from it CREATE QUEUE dbo.SSBSLearningTargetQueue WITH STATUS = ON, ACTIVATION ( STATUS = ON, PROCEDURE_NAME = Prc_RequestQueueProcessor, MAX_QUEUE_READERS = 10, EXECUTE AS SELF ), POISON_MESSAGE_HANDLING ( STATUS = OFF ); GO |
Writing Queue Activation Stored Procedure
Please note the stored procedure, which you specified as an activation stored procedure of the queue while creating it, must exist before creating the queue or else you will get a Msg 2812, Level 16, State 64, Line 3 Could not find stored procedure ‘Prc_RequestQueueProcessor’ exception.
Writing activation stored procedures is not very different than the code that we used to retrieve the message from the queue; we use the same code to retrieve the message but this time in loop and we terminate this loop when the message is not retrieved. This procedure is called/activated by Service Broker Queue Monitor whenever a message arrives in the queue. During execution or run time, this stored procedure retrieves messages for processing from the queue in a loop, as long as it gets messages in the queue. In other words, the loop terminates (execution of the stored procedure stops) when all messages are processed or messages are exhausted or no more messages are available in the queue.
Writing Queue Activation Stored Procedure |
USE SSBSTargetDB; GO CREATE PROCEDURE [dbo].[Prc_RequestQueueProcessor] AS BEGIN BEGIN TRY DECLARE @SSBSTargetDialogHandle UNIQUEIDENTIFIER; DECLARE @RecvdRequestMessage XML; DECLARE @RecvdRequestMessageTypeName sysname; SET NOCOUNT ON; WHILE (1=1) BEGIN BEGIN TRANSACTION; –WAITFOR command is used to wait for messages to arrive –on the queue, TIMEOUT is specified in miliseconds WAITFOR ( RECEIVE TOP(1) @SSBSTargetDialogHandle = conversation_handle, @RecvdRequestMessage = CONVERT(XML, message_body), @RecvdRequestMessageTypeName = message_type_name FROM dbo.SSBSLearningTargetQueue ), TIMEOUT 1000;
IF (@@ROWCOUNT = 0) BEGIN IF (@@TRANCOUNT > 0 ) ROLLBACK TRANSACTION; BREAK; END
IF @RecvdRequestMessageTypeName = N’//SSBSLearning/ProductStockStatusCheckRequest’ BEGIN DECLARE @ReplyMessage NVARCHAR(max); DECLARE @Quantity smallint SELECT @Quantity = Quantity FROM AdventureWorks.Production.ProductInventory WHERE ProductID = @RecvdRequestMessage.value(‘(/Request/ProductID)[1]’, ‘int’) AND LocationID = @RecvdRequestMessage.value(‘(/Request/LocationID)[1]’, ‘int’) SELECT @ReplyMessage = N'<Reply> <Quantity>’ + CONVERT(VARCHAR(10), @Quantity) + ‘</Quantity> </Reply>’; –To send message back to sender you again use SEND command and specify the dialog –handle which you got above while retrieving the message from the queue SEND ON CONVERSATION @SSBSTargetDialogHandle MESSAGE TYPE [//SSBSLearning/ProductStockStatusCheckResponse] (@ReplyMessage); –To end a dialog you use END CONVERSATION command, here the dialog –is being ended by Target, and then a message of — http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog message type –will be sent to Initiator END CONVERSATION @SSBSTargetDialogHandle; END ELSE IF @RecvdRequestMessageTypeName = N’http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog’ BEGIN –If the conversation is ended by the Initiator then –it also needs to be ended from Target as well END CONVERSATION @SSBSTargetDialogHandle; END ELSE IF @RecvdRequestMessageTypeName = N’http://schemas.microsoft.com/SQL/ServiceBroker/Error’ BEGIN –If there is any error message on the queue from the conversation –this conversation needs to be ended END CONVERSATION @SSBSTargetDialogHandle; END
COMMIT TRANSACTION; END END TRY BEGIN CATCH IF (@@TRANCOUNT > 0 ) ROLLBACK TRANSACTION;
DECLARE @ErrMessage VARCHAR(MAX) SET @ErrMessage = ‘ErrorNumber – ‘ + CONVERT(VARCHAR(100), ERROR_NUMBER()) + ‘ : ErrorMessage – ‘ + ERROR_MESSAGE()
–RAISERROR(@ErrMessage, 16, 1) END CATCH END |
Similar to the way we created an activation stored procedure to process request messages arriving in the target queue, we can also create an activation stored procedure to process the response messages arriving in the sender queue.
If for any reason you want to disable the activation on a queue, you can use either of these statements below.
ALTER QUEUE <QueueName>
WITH ACTIVATION
(
STATUS = OFF
);
ALTER QUEUE <QueueName>
WITH ACTIVATION
(
MAX_QUEUE_READERS = 0
);
In this article, I talked about setting up internal activation and writing a stored procedure, which was called on activation. In the next article, I will discuss external activation in detail.
Resources
MSDN SQL Server Service Broker
MSDN Service Broker Activation
Security Context of Service Broker Internal Activation