I don’t work often with SQL Server replication. The main reason is that the number of customers that use replication is pretty low and each time that I have to deal with it, it’s a good opportunity to improve my skills on this area. A couple of months ago I had to face an interesting issue with the snapshot replication (yes, I was lucky … this is not the more complex replication mechanism you have to deal with). My customer had a lot of databases (approximatively 250) to replicate from two datacenters apart from one continent.
The global architecture includes two servers that act as publishers with a total of 250 articles (one table into one database) and a central subscriber that gathers this 250 articles (250 databases with one table into each database). All articles are concerned by push subscriptions and all replication jobs are running on the publishers. This replication process is part of a more wide ETL process which delivers some financial metric reports to the business users.
Before computing the financial metrics we need to ensure that all databases replication is done correctly. Unfortunately, according to the customer security context we are stuck because we may not have access from the different publishers. So the main question that remains is how to ensure that we don’t start the calculation of the financial metrics, if we detect that there exist running database replication processes at the same time from the subscriber side?
After reflexion, I had a solution but it is not perfect. I will explain why later in this blog post. My solution includes two things:
1- Recording the last time the concerned table is created. As reminder snapshot replication will recreate concerned articles on the subscriber.
2- Tracking bulk insert activity issued by the snapshot agent before performing the calculation of the financial metrics
Here my script:
SELECT
DB_NAME() as database_name,
tb.name as table_name,
tb.create_date as last_creation_date,
txt.text as current_statement,
txt.program_name as interface,
txt.request_mode,
txt.request_owner_type,
txt.request_status
FROM sys.tables as tb
OUTER APPLY(
SELECT
db_name(r.database_id) as database_name,
t.text,
s.program_name,
l.request_mode,
l.request_status,
l.request_owner_type
FROM sys.dm_exec_requests AS r
JOIN sys.dm_exec_sessions as s
on r.session_id = s.session_id
JOIN sys.dm_tran_locks as l
on l.request_session_id = s.session_id
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) AS t
WHERE t.text LIKE ‘insert bulk % + tb.name + ”%TABLOCK%’
AND r.session_id @@SPID
AND l.resource_type = ‘OBJECT’
AND l.resource_database_id = r.database_id
AND l.resource_associated_entity_id = tb.object_id
) AS txt
WHERE tb.name = ‘mytable’
I finally created a stored procedure that fetches each concerned database and execute the above script in the current context of the database. In the context of my customer I implemented an additional step which sends an email on half-day basis. Notice also that I added a custom business rule that detects replication issue if it does not occur before the last 6 hours. You can modify and adjust the script at your convenience.
SET NOCOUNT ON;
DECLARE @database_name SYSNAME;
DECLARE @sql NVARCHAR(MAX);
— working table : alert_replication_monitoring
TRUNCATE TABLE msdb.dbo.alert_replication_monitoring;
— for each concered database we will verify if the t_replixxx table is updated
— from snapshot replication
DECLARE C_DB CURSOR FAST_FORWARD FOR
SELECT
name
FROM sys.databases
WHERE name LIKE ‘repli_%’;
OPEN C_DB;
FETCH NEXT FROM C_DB INTO @database_name;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @sql = N’USE ‘ + QUOTENAME(@database_name) + ‘
IF EXISTS (SELECT 1
FROM sys.tables WHERE name LIKE N”t_repli%”)
BEGIN
INSERT INTO msdb.dbo.alert_replication_monitoring
SELECT
DB_NAME() as database_name,
tb.name as table_name,
tb.create_date as last_creation_date,
txt.text as current_statement,
txt.program_name as interface,
txt.request_mode,
txt.request_owner_type,
txt.request_status
FROM sys.tables as tb
OUTER APPLY (
SELECT
db_name(r.database_id) as database_name,
t.text,
s.program_name,
l.request_mode,
l.request_status,
l.request_owner_type
FROM sys.dm_exec_requests AS r
JOIN sys.dm_exec_sessions as s
on r.session_id = s.session_id
JOIN sys.dm_tran_locks as l
on l.request_session_id = s.session_id
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) AS t
WHERE t.text LIKE ”insert bulk %” + tb.name + ”%TABLOCK%”
AND r.session_id @@SPID
AND l.resource_type = ”OBJECT”
AND l.resource_database_id = r.database_id
AND l.resource_associated_entity_id = tb.object_id
) AS txt
WHERE tb.name LIKE ”t_repli%”
END’;
EXEC sp_executesql@sql;
FETCH NEXT FROM C_DB INTO @database_name;
END
CLOSE C_DB;
DEALLOCATE C_DB;
— Check status of each database replication
SELECT *
FROM
(
SELECT
database_name AS [database],
table_name AS [table],
last_creation_date AS [last synchro],
DATEDIFF(MI, last_creation_date, CURRENT_TIMESTAMP) AS [time since the last update (minutes)],
current_statement,
CASE
WHEN DATEDIFF(MI, last_creation_date, CURRENT_TIMESTAMP)
AND current_statement IS NOT NULL THEN ‘1 – Replication in progress.’
WHEN DATEDIFF(MI, last_creation_date, CURRENT_TIMESTAMP)
AND current_statement IS NULL THEN ‘2 – Replication done.’
WHEN DATEDIFF(MI, last_creation_date, CURRENT_TIMESTAMP) > 360
AND current_statement IS NOT NULL THEN ‘3 – Replication in progress and takes longer than expected (6 hours).’
ELSE ‘4 – No replication has occured during the six last hours.’
END replication_status
FROM msdb.dbo.alert_replication_monitoring
) AS repli
ORDER BY [database], replication_status DESC;
Here a picture of the script result while the snapshot agent is running…
… and when there is no activity but we track the last time the table was synchronized.
As I said earlier, this method has a main following caveat:
We may only claim that a replication process is not running at a given time but we may not know if the replication process is done correctly or with errors.
Remember that this is more a workaround than a perfect solution.
Hope it helps! Please feel free to share your comments about this script!
By David Barbarin
Post Views: 654