As you probably know, the basic triggers are insert ,update & delete triggers, that potentially allow us to react to every inserted/deleted/updated row.
One of the main problems with triggers starts when we attach to those events heavy processing.
One way to reduce the time the user waits for the related transaction to end is to change the behavior of those triggers to be asynchronous.
The main idea is to build an xml in the trigger and pass it to an sqlBroker queue for later processing, well..it doesn't have to be 'later', but it is async so it releases the main thread to continue & return to the client.
Let's look at the code, first we'll start with the declaration stuff:
--Enable broker SET @strsql = 'ALTER DATABASE ' + db_name() + ' SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE ' EXEC sp_executesql @strsql -- Create Message Type CREATE MESSAGE TYPE SampleMessage AUTHORIZATION dbo VALIDATION = WELL_FORMED_XML; -- Create Contract CREATE CONTRACT SampleContract AUTHORIZATION dbo (SampleMessage SENT BY INITIATOR); -- Create Queue CREATE QUEUE dbo.SampleQueue WITH STATUS=ON, ACTIVATION (STATUS = ON, MAX_QUEUE_READERS = 1, PROCEDURE_NAME = spMessageProcSample, EXECUTE AS OWNER); -- Create init Service CREATE SERVICE SampleServiceInitiator AUTHORIZATION dbo ON QUEUE dbo.SampleQueue (SampleContract); -- Create target Service CREATE SERVICE [SampleServiceTarget] AUTHORIZATION dbo ON QUEUE dbo.SampleQueue (SampleContract);
The trigger will use the SampleServiceInitiator to send an xml message (SampleMessage message type), this message will reach the SampleQueue queue which will be retrieved by SampleServiceTarget, this service will run the stored procedure named spMessageProcSample to handle the message.
Let's see a sample trigger code:
CREATE TRIGGER [trigSample] ON [dbo].SampleTable FOR UPDATE As SET NOCOUNT ON; DECLARE @MessageBody XML DECLARE @TableId int --get relevant information from inserted/deleted and convert to xml message SET @MessageBody = (SELECT col1, col2 FROM inserted FOR XML AUTO) If (@MessageBody IS NOT NULL) BEGIN DECLARE @Handle UNIQUEIDENTIFIER; BEGIN DIALOG CONVERSATION @Handle FROM SERVICE [SampleServiceInitiator] TO SERVICE 'SampleServiceTarget' ON CONTRACT [SampleContract] WITH ENCRYPTION = OFF; SEND ON CONVERSATION @Handle MESSAGE TYPE [SampleMessageType](@MessageBody); END
Sample code of the reciever sp:
CREATE PROCEDURE [dbo].[spMessageProcSample] AS BEGIN DECLARE @message_type varchar(100) DECLARE @dialog uniqueidentifier, @message_body XML; WHILE (1 = 1) BEGIN -- Receive the next available message from the queue WAITFOR ( RECEIVE TOP(1) @message_type = message_type_name, @message_body = CAST(message_body AS XML), @dialog = conversation_handle FROM dbo.SampleQueue ), TIMEOUT 500 if (@@ROWCOUNT = 0 OR @message_body IS NULL) BEGIN BREAK END ELSE BEGIN --process xml message here... select ref.value('@SampleColumn[1]', 'int'), from @message_body.nodes('//SampleMessage') T(ref) END END CONVERSATION @dialog END END
That's it...some new objects to know and a bit weird syntax..but it does the work :-)
have fun
Diego