Register
Sunday, February 05, 2012
 
 DBAs And ProgrammersBlog
  
News! Minimize
   
 
 Print   
 
Misc Blog Stuff Minimize
   
 
 Print   
 
The Reluctant DBA Minimize
 
 
 
 Print   
 
Reluctant DBA Minimize
   
 
  
 
Reluctant DBA Minimize
   
 
  
 
The Reluctant DBA Minimize
 
Mar9

Written by:CarpDeus
3/9/2009 9:27 AM 

I'm not a fan of database triggers. Never have been, truth to tell, even when they were the only way to handle data integrity. A little over a decade ago, I was involved with a fairly complex project for handling order fulfillment. A lot of the tables had triggers where an insert in table A kicked off more inserts and cascaded with business logic. And there was a problem, an intermittent bug that was difficult to reproduce where parts of orders were being lost. Turns out that you can only nest triggers 32 deep, and we sometimes were trying to kick of up to 35 levels of triggers. Add in that triggers are blocking and it gets even messier.

I mention that because I am in the process of having to deal with triggers again. We are going to be upgrading an old version of our company's internal framework to the new version and it's going to be a seamless blending of old and new, where users can exist on both frameworks and changes to their profiles on one framework need to flow into the other framework. Over three separate servers.

The data needs to flow between two database in the new framework on one server and two databases on different servers for the old framework. So I get to recreate replication and I'm planning to implement it with triggers and service broker.

So a trigger on each table that needs to feed the replication process will write an XML version of the inserted and deleted tables to a separate database that exists solely for tracking the changes:

-- =============================================
-- Author:        Josef Finsel
-- Create date: 2009-03-06
-- Description:    
-- =============================================
ALTER TRIGGER [dbo].[Registrations_Trigger] 
   ON  [dbo].[Registrations]
   AFTER INSERT,DELETE,UPDATE
AS 
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @InsertXML xml
    DECLARE @DeleteXML xml
    SET @InsertXML = (SELECT * FROM Inserted FOR XML AUTO, ELEMENTS, ROOT('Inserted'))
    SET @DeleteXML = (SELECT * FROM Deleted FOR XML AUTO, ELEMENTS, ROOT('Deleted'))
    declare @CommandData XML
    SET @CommandData = convert(xml,'' + convert(nvarchar(max), ISNULL(@InsertXML, '')) 
        + convert(nvarchar(max), ISNULL(@DeleteXML, '')) + '
')
    INSERT INTO OurReplication.dbo.ReplicationQueue(ReplicationQueueID, FromTable, ToTable, CommandType, 
        CommandData, CreateDate, ProcessedDate, ErrorInfo, ProcessedFlag, HashCode)
    VALUES(NewID(), Old.Registrations', 'New.UserProfiles','P', 
        @CommandData, getdate(), NULL, NULL,  'N', NULL)

END

This is a simplified version of the trigger since I really need to use XML EXPLICIT to handle CDATA on data that can contain & and > or <. And there will be a service running to take the data entered into the ReplicationQueue table and actually process it. To prevent Replication updates echoing back to the original table, there's a new uniqueidentifier column on each of the tables. The only time that this column will be included is when the Replication code gets executed. When the service is processing the XML in the ReplicationQueuetable, it will ignore any data that has that column populated in the InsertXML.

I started talking about nested triggers because I want to use Service Broker to tell the Windows Service that there's something to process. Since this is in a different database than the one that the trigger generating the data is in, the only way to add data to the broker is to add a trigger to the ReplicationQueue table:

-- =============================================
-- Author:        Josef Finsel
-- Create date: 2009-03-09
-- Description:    
-- =============================================
CREATE TRIGGER ReplicationQueueServiceBrokerCall
   ON  ReplicationQueue
   AFTER INSERT
AS 
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;
    DECLARE @msg XML
    --DECLARE @body nvarchar(1000)
    SET @msg = (select ReplicationQueueID FROM Inserted
                FOR XML AUTO, ELEMENTS, ROOT('ReplicationRequest'))

    DECLARE @dialogHandle uniqueidentifier
    BEGIN DIALOG CONVERSATION @dialogHandle
        FROM SERVICE [InitiateReplicationRequestService]
        TO SERVICE 'ProcessReplicationRequestService'
        ON CONTRACT [ProcessReplicationRequestContract];
    SEND ON CONVERSATION @dialogHandle
        MESSAGE TYPE [ProcessReplicationRequestMessage]
            (@msg)

    END CONVERSATION @dialogHandle

END

So, that's a really high-level view but what have I missed that is going to cause me issues? Thoughts? Comments?

Copyright ©2009 Carp Deus

Tags:

Your name:
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
Enter the code shown above in the box below
Add Comment  Cancel 
 
 
  
 
Privacy Statement | Terms Of Use Copyright 2001-2008 by ReluctantDBA.com