SQLServerCentral Article

Centrally collect replication conflicts and send an alert

,

While working with merge replication, I found it tedious to have to go to every publication server and view the replication conflicts using the "view conflicts" tool that is provided with SQL Server Management Studio.Plus, call me lazy, but I like an email notification for all repeatable or tedious tasks that could be automated. I looked on the web for solutions already out there, but I found nothing, so I have created a process that will collect all of the replication conflicts into a central database for each publisher and then send out an email notification. In my case, I have 1 main publisher, 2 re-publishers and 15 subscriptions for replication, so I generally have several conflicts a week to deal with. This process consists of the following:

  • 1 Job to collect data (on each publisher)
  • 1 SSIS package to move the data from flat files to tables
  • 1 main Job to call the SSIS package (on the centralized server)
  • 1 report Job to create the alert that will be called by the main job above.
  • 2 tables to house the conflict information (on the centralized server)

I have created a database that I use for my DBA activities that I call DBA_Reports, however, you can call yours whatever you like just remember to change it in the scripts below. Please make sure that you have the following before you begin:

  • DBMail enabled on your server(s) - this is absolutely key to this process
  • Depending how you are set up and what servers you are watching, you may need to create a linked server for each server you are sharing data with.
  • Create yourself as an operator so you can receive notifications if your job fails. Personally I like to add a notification on every job I create in case it fails. This is optional.

I'm not going to go into details on how to set these up as they are readily available from other sources, however, just consider them as prerequisites.

Step One: Create the tables

The table PublisherConflict_counts_stage is used as the initial holding place where the first round of data is held. This is populated by the SSIS package Get Replication Conflicts that will be discussed in detail in step three.

CREATE TABLE [dbo].[PublisherConflict_counts_stage](
[article] [varchar](max) NOT NULL,
[source_object] [varchar](max) NOT NULL,
[conflict_table] [varchar](max) NOT NULL,
[guidcolname] [varchar](max) NOT NULL,
[centralized_conflicts] [varchar](max) NOT NULL,
[conflicts_ucount] [varchar](25) NULL,
[conflicts_dcount] [varchar](25) NULL
) ON [PRIMARY]

The PublisherConflicts_counts table is used as the final holding place for the conflict data and alert generation as discussed in step three. Data is moved from the PublisherConflict_counts_stag table by the SSIS package Get Replication Conflicts and then the table is updated with the server name and database name for each record.

CREATE TABLE [dbo].[PublisherConflicts_counts](
[article] [varchar](max) NOT NULL,
[source_object] [varchar](max) NOT NULL,
[conflict_table] [varchar](max) NOT NULL,
[guidcolname] [varchar](max) NOT NULL,
[centralized_conflicts] [varchar](max) NOT NULL,
[conflicts_ucount] [varchar](25) NULL,
[conflicts_dcount] [varchar](10) NULL,
[servername] [varchar](100) NULL,
[databasename] [varchar](100) NULL
) ON [PRIMARY]

Step Two: Generate the Flat file using Get Conflicts Job

The Get Conflicts job will run exec sp_MShelpmergeconflictcounts on the publisher database and write the results out to a file. I have my file being saved to my central location, however put yours where ever you like, just make sure you can access it from your central server. I schedule mine to run every 2 hours, again the schedule is up to you. This job will need to run on each publisher. Copy/paste the script below, make sure to change the areas marked <> with your data.

USE [msdb]
GO
BEGIN TRANSACTION
DECLARE @ReturnCode INT
SELECT @ReturnCode = 0
IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'Data Collector' AND category_class=1)
BEGIN
EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'Data Collector'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
END
DECLARE @jobId BINARY(16)
EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'DBA-Reports - Get Conflicts',
@enabled=1,
@notify_level_eventlog=0,
@notify_level_email=0,
@notify_level_netsend=0,
@notify_level_page=0,
@delete_level=0,
@description=N'Collects conflict information into flat files to be used in reporting',
@category_name=N'Data Collector',
@owner_login_name=N'sa', @job_id = @jobId OUTPUT
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Get Conflicts',
@step_id=1,
@cmdexec_success_code=0,
@on_success_action=3,
@on_success_step_id=0,
@on_fail_action=2,
@on_fail_step_id=0,
@retry_attempts=0,
@retry_interval=0,
@os_run_priority=0, @subsystem=N'TSQL',
@command=N'exec sp_MShelpmergeconflictcounts',
@database_name=N'<add your database here>',
@output_file_name=N'\\<add your location here>\Conflicts.txt',
@flags=0
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobschedule @job_id=@jobId, @name=N'Daily',
@enabled=1,
@freq_type=8,
@freq_interval=62,
@freq_subday_type=1,
@freq_subday_interval=0,
@freq_relative_interval=0,
@freq_recurrence_factor=1,
@active_start_date=20110727,
@active_end_date=99991231,
@active_start_time=120000,
@active_end_time=235959
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION
EndSave:

Job: Get Replication Publisher Conflicts

The Get Replication Publisher Conflicts job calls the SSIS package named Get Replication Conflicts in step one where the data from the files is pulled into the tables. In step two, it will call the job DBA-Reports - Report Get Replication Publisher Conflicts which will send out the email notification; this job will be discussed next. I have this scheduled to run once per day. However if you have a lot of conflicts to deal with throughout a day, this schedule could be increased to what ever you want. Copy/Paste/Run the script below to create the job and remember to change the information in <> to your information.

USE [msdb]
GO
BEGIN TRANSACTION
DECLARE @ReturnCode INT
SELECT @ReturnCode = 0
IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'DBA Monitoring' AND category_class=1)
BEGIN
EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'DBA Monitoring'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
END
DECLARE @jobId BINARY(16)
EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'DBA-Reports - Get Replication Publisher Conflicts',
@enabled=1,
@notify_level_eventlog=0,
@notify_level_email=0,
@notify_level_netsend=0,
@notify_level_page=0,
@delete_level=0,
@description=N'Collects the replication conflicts from each publisher then consolidates them into a table.
Sends out an email notification',
@category_name=N'DBA Monitoring',
@owner_login_name=N'sa', @job_id = @jobId OUTPUT
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Consolidate the data from all publishers',
@step_id=1,
@cmdexec_success_code=0,
@on_success_action=3,
@on_success_step_id=0,
@on_fail_action=2,
@on_fail_step_id=0,
@retry_attempts=0,
@retry_interval=0,
@os_run_priority=0, @subsystem=N'SSIS',
@command=N'/SQL "\Get Replication Conflicts" /SERVER <add server name> /CHECKPOINTING OFF /REPORTING E',
@database_name=N'SQL_Overview'
--@output_file_name=N'E:\Reports\GetConflictsErrors.txt',
@flags=0
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Send Email',
@step_id=2,
@cmdexec_success_code=0,
@on_success_action=1,
@on_success_step_id=0,
@on_fail_action=2,
@on_fail_step_id=0,
@retry_attempts=0,
@retry_interval=0,
@os_run_priority=0, @subsystem=N'TSQL',
@command=N'EXEC msdb.dbo.sp_start_job ''DBA-Reports - Report Get Replication Publisher Conflicts''',
@database_name=N'<Add Your DB here>',
@flags=0
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobschedule @job_id=@jobId, @name=N'daily',
@enabled=1,
@freq_type=8,
@freq_interval=62,
@freq_subday_type=1,
@freq_subday_interval=0,
@freq_relative_interval=0,
@freq_recurrence_factor=1,
@active_start_date=20110727,
@active_end_date=99991231,
@active_start_time=150000,
@active_end_time=235959
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION
EndSave:

Job: Report Get Replication Publisher Conflicts

The Report Get Replication Publisher Conflicts job will send out an email alert of the conflicts collected. I have mine scheduled to run once per day. This is a great reminder for DBA's to resolve conflicts and is the heart of this process. I have this report in a separate job so I can run it anytime after the initial run that is called from Get Replication Publisher Conflicts. This job uses database mail within SQL Server, so make sure you have that setup prior to running. Copy /Paste/Run the script below to create the job and make sure to change the text in <> to your information.

USE [msdb]
GO
BEGIN TRANSACTION
DECLARE @ReturnCode INT
SELECT @ReturnCode = 0
IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'[Uncategorized (Local)]' AND category_class=1)
BEGIN
EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'[Uncategorized (Local)]'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
END
DECLARE @jobId BINARY(16)
EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'DBA-Reports - Report Get Replication Publisher Conflicts',
@enabled=1,
@notify_level_eventlog=0,
@notify_level_email=2,
@notify_level_netsend=0,
@notify_level_page=0,
@delete_level=0,
@description=N'Sends an email of publisher conflicts from all publishers',
@category_name=N'[Uncategorized (Local)]',
@owner_login_name=N'sa',
@notify_email_operator_name=N'<Add your operator>', @job_id = @jobId OUTPUT
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'send email',
@step_id=1,
@cmdexec_success_code=0,
@on_success_action=1,
@on_success_step_id=0,
@on_fail_action=2,
@on_fail_step_id=0,
@retry_attempts=0,
@retry_interval=0,
@os_run_priority=0, @subsystem=N'TSQL',
@command=N'SET nocount ON
--
DECLARE @Subject VARCHAR (100)
SET @Subject=''SQL Server - Replication Conflicts on Publishers''
DECLARE @Count AS INT
SELECT @Count=COUNT(*) FROM [SQL_Overview].[dbo].[PublisherConflicts_counts
PRINT @Count
IF @Count > 0
BEGIN
DECLARE @tableHTML NVARCHAR(MAX) ;
SET @tableHTML =
N''<table border="1">'' +
N''<tr>'' +
N''<th>Server</th>'' +
N''<th>Database</th>'' +
N''<th>Article</th>'' +
N''<th>Conflicts</th>'' +
N''</tr>'' +
CAST ( ( SELECT td=[servername],''''
,td=[databasename],''''
,td=[article],''''
,td=(cast([conflicts_ucount] as int) + cast([conflicts_dcount] as int)),''''
,''''
FROM [SQL_Overview].[dbo].[PublisherConflicts_counts]
ORDER BY Servername
FOR XML PATH(''tr''), TYPE
) AS NVARCHAR(MAX) ) +
N''</table>'' ;
EXEC msdb.dbo.sp_send_dbmail
@profile_name = ''<use your info>'',
@recipients = ''<use your info>'',
@subject = @Subject,
@body = @tableHTML,
@body_format = ''HTML'' ;
END',
@database_name=N'DBA_Reports',
@flags=0
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION
EndSave:

Step Three: SSIS Package - Get Replication Conflicts

In the SSIS package called Get Replication Conflicts, this is a simple process that imports data from flat files, then tweeking the data by adding the database name and server name of the source record before moving the data on to its final destination. The package is fairly simple with only 3 main parts plus 3 data flowpieces. In my case, as I stated previously, I have 1 main publisher and 2 re-publishers, so for each server, I have the following in my package.

Connections(1 for each publisher flat file generated):

  • SourceConnectionFlatFile_MainPublisher
  • SourceConnectionFlatFile_Publisher1
  • SourceConnectionFlatFile_Publisher2

This is the connection to the flat file generated in step 2 from the job called Get Conflicts. You will need one of these for each publisher you plan on collecting data for.

Central Server Connection

This the connection to the database for the central server.

Once you have your connections created, continue to create the package. On the control flow create a SQL task to truncate the tables with the following two lines:

truncate table [dbo].[PublisherConflict_counts_stage]
truncate table [dbo].[PublisherConflicts_counts]

I like to start fresh with every run of data since data changes daily, there is no point in retaining the data. If you only have a single publisher, then above is what your package will look like. If you have multiple publishers like me, then you will need a set of these branches for each publisher plus one additional SQL Task that truncate table [dbo].[PublisherConflict_counts_stage] so that you do not re-insert duplicates. This step follows the Add DB and Server Names and move to final table or basically it goes between each set of branches(publishers).

Next create a data flow task to move data from the generated flat files into the PublisherConflict_counts_stage table.

Add a flat file source and point it to your flat file connection. 

Create a data conversion task. Make sure you change the conflicts_ucount and conflicts_dcount data type to string/length 25 so it matches our table definition as created in step one.

Finally create the OLE Destination task. This will move the data into our table.

The final step on the control flow is to add a sql task that will add the database and server name.

INSERT INTO [SQL_Overview].[dbo].[PublisherConflicts_counts]
([article]
,[source_object]
,[conflict_table]
,[guidcolname]
,[centralized_conflicts]
,[conflicts_ucount]
,[conflicts_dcount]
,[servername]
,[databasename])
(SELECT [article]
,[source_object]
,[conflict_table]
,[guidcolname]
,[centralized_conflicts]
,[conflicts_ucount]
,[conflicts_dcount],
'<your server>',
'<your database>'
FROM [SQL_Overview].[dbo].[PublisherConflict_counts_stage])

When this package is completed and saved, import it into SSIS.

Conclusion

Now you have a way to capture replication conficts and receive an alert of the replication publisher conflicts automatically.

Included in this package, the following will be created:

Tables:

- dbo.PublisherConflict_counts_stage

- dbo.PublisherConflicts_counts

Jobs

- DBA-Reports - Get Replication Publisher Conflicts

- DBA-Reports - Report Get Replication Publisher Conflicts

- DBA-Reports - Get Conflicts

SSIS Package

- Get Replication Conflicts

Resources

Rate

3.8 (5)

You rated this post out of 5. Change rating

Share

Share

Rate

3.8 (5)

You rated this post out of 5. Change rating