/*============================================================================= 

 User guide 
  ---------------------------------------------------

  01. Create Table [Replication_queue_history]
  02. Create Indexes [NCIX_?]
  03. Create stored procedure [Get_replication_counters]
  04. Create Job [DR92280 - Monitoring SQL Server Replication]
  
 Usage context
  ------------------------------------------
    To collect performance status for all publication's subscriptions in the server  

     
 Parameters
  ------------------------------------------

 Creation : 27.10.21 / RTC

 Modification : 17.03.2022 - FLA : Change DBA mail
                22.04.2022 - FLA : Correct SP location in job

=============================================================================*/ 


/***************************************************************/
/******      Create Table [Replication_queue_history]    *******/
/***************************************************************/
USE [HCITools]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[mon].[Replication_queue_history]') AND type in (N'U'))
BEGIN

CREATE TABLE mon.Replication_queue_history(
	   Replication_queue_history_Id INT IDENTITY(1,1) NOT NULL,
       RQH_subscriber_db VARCHAR(50) NOT NULL,
       RQH_records_in_queue NUMERIC(18, 0) NULL,
       RQH_catch_up_time NUMERIC(18, 0) NULL,
       RQH_log_date DATETIME NOT NULL
 CONSTRAINT PK_EPR_Replication_queue_history PRIMARY KEY CLUSTERED 
(
       RQH_subscriber_db ASC, RQH_log_date DESC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
END
GO

/***************************************************************/
/******   Create Procedure [Get_replication_counters]    *******/
/***************************************************************/

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[mon_Get_Replication_Counters]') AND type in (N'P', N'PC'))
 DROP PROCEDURE [dbo].[mon_Get_Replication_Counters]
GO

CREATE PROCEDURE [dbo].[mon_Get_Replication_Counters]
					@in_debug INT = NULL
AS

/*============================================================================= 

  Explication du traitement realise par la SP
  -------------------------------------------
    To collect performance counters in SQL Server Replication
 
  Contexte d'utilisation
  ----------------------
    Job : DR92280 - Monitoring SQL Server Replication
 
  Parametres  
  ----------  
    @in_debug      
 
  Creation : 27.10.2021 / RTC
 
  Modifications : 
   
=============================================================================*/ 

DECLARE @cmd NVARCHAR(max)
DECLARE @publisher    SYSNAME,
        @publisher_db SYSNAME,
        @publication  SYSNAME,
        @pubtype      INT
DECLARE @subscriber    SYSNAME,
        @subscriber_db SYSNAME,
        @subtype       INT
DECLARE @cmdcount    INT,
        @processtime INT
DECLARE @ParmDefinition NVARCHAR(500)
DECLARE @JobName SYSNAME
DECLARE @minutes     INT,
        @threshold   INT,
        @maxCommands INT,
        @mail        CHAR(1) = 'N'
SET @minutes = 60 --> Define how many minutes latency before you would like to be notified
SET @maxCommands = 80000 --->  change this to represent the max number of outstanding commands to be proceduresed before notification
SET @threshold = @minutes * 60

/* Drop all temp tables */
IF OBJECT_ID(N'tempdb..#PublisherInfo') is not null
    drop table #PublisherInfo

if exists (SELECT *
                FROM [TempDB].sys.objects o WITH (NOLOCK)
               WHERE o.name = '##PublicationInfo' 
			     AND o.type IN (N'U'))
				 Drop table ##PublicationInfo


if exists (SELECT *
                FROM [TempDB].sys.objects o WITH (NOLOCK)
               WHERE o.name = '##SubscriptionInfo' 
			     AND o.type IN (N'U'))
				 Drop table ##SubscriptionInfo

SELECT *
INTO   #PublisherInfo
  FROM
       OPENROWSET('SQLOLEDB',
                  'SERVER=(LOCAL);TRUSTED_CONNECTION=YES;',
                  'SET FMTONLY OFF EXEC distribution.dbo.sp_replmonitorhelppublisher WITH RESULT SETS ((publisher sysname, Distribution_db  sysname, status int, warning int, publication_count int, returnstamp bigint))') --

select *
  from #PublisherInfo

SELECT @publisher = publisher
  FROM #PublisherInfo

SET @cmd
    = 'SELECT * INTO ##PublicationInfo FROM OPENROWSET(''SQLOLEDB'',''SERVER=(LOCAL);TRUSTED_CONNECTION=YES''
,''SET FMTONLY OFF EXEC distribution.dbo.sp_replmonitorhelppublication @publisher= ''''' + @publisher
      + ''''' with result sets ((publisher_db sysname, publication sysname, publication_id int, publication_type int, status int, warning int, worst_latency int, best_latency int, average_latency int, last_distsync datetime,
 retention int, latencythreshold int, expirationthreshold int, agentnotrunningthreshold int, subscriptioncount int, runningdistagentcount int, snapshot_agentname sysname, logreader_agentname sysname, 
 qreader_agentname sysname, worst_runspeedPerf int, best_runspeedPerf int, average_runspeedPerf int, retention_period_unit int, publisher sysname))'')'

--select @cmd

EXEC sp_executesql @cmd

select *
  FROM ##PublicationInfo


SELECT @publisher_db = publisher_db,
       @publication = publication,
       @pubtype = publication_type
  FROM ##PublicationInfo

SET @cmd
    = 'SELECT * INTO ##SubscriptionInfo FROM OPENROWSET(''SQLOLEDB'',''SERVER=(LOCAL);TRUSTED_CONNECTION=YES''
,''SET FMTONLY OFF EXEC distribution.dbo.sp_replmonitorhelpsubscription @publisher=''''' + @publisher
      + ''''',@publication_type=' + CONVERT(CHAR(1), @pubtype)
      + ' with result sets ((status int, warning int, subscriber sysname, subscriber_db sysname, publisher_db sysname, 
publication sysname, publication_type int, subtype int, latency int, latencythreshold int, agentnotrunning int, agentnotrunningthreshold int, timetoexpiration int, expirationthreshold int,
 last_distsync datetime, distribution_agentname sysname, mergeagentname sysname, mergesubscriptionfriendlyname sysname, mergeagentlocation sysname, mergeconnectiontype int, mergePerformance int,
 mergerunspeed float,  mergerunduration int,  monitorranking int, distributionagentjobid binary(16), mergeagentjobid binary(16), distributionagentid int, distributionagentprofileid int,
 mergeagentid int, mergeagentprofileid int, logreaderagentname sysname, publisher sysname   ))'')'

--select @cmd

EXEC sp_executesql @cmd

select *
  from ##SubscriptionInfo


ALTER TABLE ##SubscriptionInfo
ADD PendingCmdCount INT NULL,
    EstimatedProcessTime INT NULL

DECLARE cur_sub CURSOR READ_ONLY FOR
SELECT @publisher,
       s.publisher_db,
       s.publication,
       s.subscriber,
       s.subscriber_db,
       s.subtype,
       s.distribution_agentname
  FROM ##SubscriptionInfo s

OPEN cur_sub
FETCH NEXT FROM cur_sub
 INTO @publisher,
      @publisher_db,
      @publication,
      @subscriber,
      @subscriber_db,
      @subtype,
      @JobName

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @cmd
        = 'SELECT @cmdcount=pendingcmdcount, @processtime=estimatedprocesstime FROM OPENROWSET(''SQLOLEDB'',''SERVER=(LOCAL);TRUSTED_CONNECTION=YES''
	        ,''SET FMTONLY OFF EXEC distribution.dbo.sp_replmonitorsubscriptionpendingcmds @publisher='''''
          + @publisher + ''''',@publisher_db=''''' + @publisher_db + ''''',@publication=''''' + @publication
          + ''''',@subscriber=''''' + @subscriber + ''''',@subscriber_db=''''' + @subscriber_db
          + ''''',@subscription_type=' + CONVERT(CHAR(1), @subtype)
          + ' With result sets ((pendingcmdcount int, estimatedprocesstime int))'')'
    SET @ParmDefinition = N'@cmdcount INT OUTPUT,
	                        @processtime INT OUTPUT'
    select @cmd
    EXEC sp_executesql @cmd,
                       @ParmDefinition,
                       @cmdcount OUTPUT,
                       @processtime OUTPUT

    SELECT @cmdcount,
           @processtime

    UPDATE ##SubscriptionInfo
       SET PendingCmdCount = @cmdcount,
           EstimatedProcessTime = @processtime
     WHERE subscriber_db = @subscriber_db

    INSERT INTO HCITools.mon.Replication_queue_history
    VALUES (@subscriber, @cmdcount, @processtime, GETDATE())

    FETCH NEXT FROM cur_sub
     INTO @publisher,
          @publisher_db,
          @publication,
          @subscriber,
          @subscriber_db,
          @subtype,
          @JobName
END

CLOSE cur_sub
DEALLOCATE cur_sub

GO

/***************************************************************/
/******      Create Procedure [Purge_repl_queue_hist]    *******/
/***************************************************************/

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[mon_purge_repl_queue_hist]') AND type in (N'P', N'PC'))
 DROP PROCEDURE [dbo].[mon_Purge_repl_queue_hist]
GO

CREATE PROCEDURE [dbo].[mon_Purge_repl_queue_hist]
					@in_debug INT = NULL
AS

/*============================================================================= 

  Explication du traitement realise par la SP
  -------------------------------------------
    To purge performance counters history in HCITools
 
  Contexte d'utilisation
  ----------------------
    Job : DR92280 - Monitoring SQL Server Replication
 
  Parametres  
  ----------  
    @in_debug      
 
  Creation : 27.10.2021 / RTC
 
  Modifications : 
   
=============================================================================*/ 
DECLARE @delDate datetime = getdate()-30
  DELETE FROM mon.Replication_queue_history
  WHERE RQH_log_date < @deldate

GO

/***************************************************************/
/**  Create Job [DR92280 - Monitoring SQL Server Replication] **/
/***************************************************************/


/* Drop existing standard schedule for job */
declare @schedule_id int
declare c_schedules cursor local forward_only static for
    select ss.schedule_id
      from msdb.dbo.sysjobschedules sjs
INNER JOIN msdb.dbo.sysschedules ss
        ON sjs.schedule_id = ss.schedule_id
       AND ss.name NOT LIKE '%#SPEC#'
INNER JOIN msdb.dbo.sysjobs sj
        ON sjs.job_id = sj.job_id
     WHERE sj.name = N'DR92280 - Monitoring SQL Server Replication'

open c_schedules

FETCH NEXT FROM c_schedules into @schedule_id
while @@fetch_status = 0
begin
  IF ((select COUNT(*) from msdb.dbo.sysjobschedules where schedule_id=@schedule_id) = 1)
    EXEC msdb.dbo.sp_delete_schedule @schedule_id=@schedule_id, @force_delete = 1
FETCH NEXT FROM c_schedules into @schedule_id
end

close c_schedules

deallocate c_schedules

IF  EXISTS (SELECT job_id FROM msdb.dbo.sysjobs_view WHERE name = N'DR92280 - Monitoring SQL Server Replication')
EXEC msdb.dbo.sp_delete_job @job_name = N'DR92280 - Monitoring SQL Server Replication', @delete_unused_schedule=0
GO

/* Creation Job and Steps*/
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

/* Add Job */
DECLARE @jobId BINARY(16)
EXEC @ReturnCode =  msdb.dbo.sp_add_job @job_name=N'DR92280 - Monitoring SQL Server Replication', 
		@enabled=1, 
		@notify_level_eventlog=0, 
		@notify_level_email=0, 
		@notify_level_netsend=0, 
		@notify_level_page=0, 
		@delete_level=0, 
		@description=N'27.10.2021 /RTC : Collect Data for replication monitoring', 
		@category_name=N'[Uncategorized (Local)]', 
		@start_step_id=1, 
		@owner_login_name=N'sa', @job_id = @jobId OUTPUT
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback

/* Add Step */
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Empty step', 
		@step_id=1, 
		@cmdexec_success_code=0, 
		@on_success_action=3, 
		@on_success_step_id=0, 
		@on_fail_action=3, 
		@on_fail_step_id=0, 
		@retry_attempts=0, 
		@retry_interval=0, 
		@os_run_priority=0, @subsystem=N'TSQL', 
		@command=N'/*Empty step */', 
		@database_name=N'master', 
		@output_file_name=NULL, 
		@flags=0, 
		@database_user_name=NULL, 
		@server=NULL, 
		@additional_parameters=NULL, 
		@proxy_id=NULL, 
		@proxy_name=NULL
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
/* Add Step */
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Repl monitor collect data', 
		@step_id=2, 
		@cmdexec_success_code=0, 
		@on_success_action=3, 
		@on_success_step_id=0, 
		@on_fail_action=4, 
		@on_fail_step_id=5, 
		@retry_attempts=0, 
		@retry_interval=0, 
		@os_run_priority=0, @subsystem=N'TSQL', 
		@command=N'exec [dbo].[mon_Get_Replication_Counters]', 
		@database_name=N'HCITools', 
		@output_file_name=NULL, 
		@flags=0, 
		@database_user_name=NULL, 
		@server=NULL, 
		@additional_parameters=NULL, 
		@proxy_id=NULL, 
		@proxy_name=NULL
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
/* Add Step */
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Purge queue history', 
		@step_id=3, 
		@cmdexec_success_code=0, 
		@on_success_action=3, 
		@on_success_step_id=0, 
		@on_fail_action=4, 
		@on_fail_step_id=5, 
		@retry_attempts=0, 
		@retry_interval=0, 
		@os_run_priority=0, @subsystem=N'TSQL', 
		@command=N'exec [dbo].[mon_Purge_repl_queue_hist]', 
		@database_name=N'HCITools', 
		@output_file_name=NULL, 
		@flags=0, 
		@database_user_name=NULL, 
		@server=NULL, 
		@additional_parameters=NULL, 
		@proxy_id=NULL, 
		@proxy_name=NULL
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
/* Add Step */
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Empty step for success', 
		@step_id=4, 
		@cmdexec_success_code=0, 
		@on_success_action=1, 
		@on_success_step_id=0, 
		@on_fail_action=3, 
		@on_fail_step_id=0, 
		@retry_attempts=0, 
		@retry_interval=0, 
		@os_run_priority=0, @subsystem=N'TSQL', 
		@command=N'/* EMpty step for success */', 
		@database_name=N'master', 
		@output_file_name=NULL, 
		@flags=0, 
		@database_user_name=NULL, 
		@server=NULL, 
		@additional_parameters=NULL, 
		@proxy_id=NULL, 
		@proxy_name=NULL
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
/* Add Step */
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Send mail KO', 
		@step_id=5, 
		@cmdexec_success_code=0, 
		@on_success_action=2, 
		@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 Get_Job_Error_Info @in_JobName = ''DR92280 - Monitoring SQL Server Replication'', @in_Recipients = ''DBA_operator''', 
		@database_name=N'HCITools', 
		@output_file_name=NULL, 
		@flags=0, 
		@database_user_name=NULL, 
		@server=NULL, 
		@additional_parameters=NULL, 
		@proxy_id=NULL, 
		@proxy_name=NULL
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

/* Add Standard Schedule */
EXEC @ReturnCode = msdb.dbo.sp_add_jobschedule @job_id=@jobId, @name=N'DR92280-DR', 
		@enabled=1, 
		@freq_type=4, 
		@freq_interval=1, 
		@freq_subday_type=4, 
		@freq_subday_interval=10, 
		@freq_relative_interval=0, 
		@freq_recurrence_factor=0, 
		@active_start_date=20210820, 
		@active_end_date=99991231, 
		@active_start_time=0, 
		@active_end_time=235959
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback

/* Attach existing specific schedule for job */
declare @enabled_schedule int,
        @schedule_name nvarchar(50)
declare c_schedules cursor local forward_only static for
select enabled, name
  from msdb.dbo.sysschedules
 where name LIKE 'DR92280%'
   and name LIKE '%#SPEC#'

open c_schedules

FETCH NEXT FROM c_schedules into @enabled_schedule, @schedule_name
while @@fetch_status = 0
begin
  EXEC @ReturnCode = msdb.dbo.sp_attach_schedule @job_id = @jobId, @schedule_name=@schedule_name
  IF(@enabled_schedule = 1)
  begin
    SET @schedule_name = SUBSTRING(@schedule_name,0,LEN(@schedule_name)-5)
    IF EXISTS (select name from msdb.dbo.sysschedules where name = @schedule_name)
      EXEC @ReturnCode = msdb.dbo.sp_update_schedule @name=@schedule_name, @enabled=0
  end
  IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
FETCH NEXT FROM c_schedules into @enabled_schedule, @schedule_name
end

close c_schedules

deallocate c_schedules

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:

GO
