Perform Automatic Switchover in Log Shipping
DR drill is one of the major activities of every DBA’s life. And if log shipping is in the picture, then we have to follow a long checklist. Many things we have to check like sync status, disable and top LS jobs on primary and secondary, get tail log backup, copy and restore the same, etc.
In such
drills, achieving RTO and RPO is very important. Most of us might have come
across the situation due to a small miss switch over or switch back got
delayed.
What if we
could automate switchover and switch back activity in log shipping. If this could
happen then we can easily achieve the RPO and RTO also human error can be avoided.
I have
tried to automate this process. I’m not too good in script writing. But I tried
my best to complete this. I have written the scripts which perform the switchover without manual intervention. We just need to provide the database name for
which switchover needs to be performed and then simply execute the script.
There are
two scripts written. One script needs to execute on the primary server and another
one needs to execute on a secondary server.
What do those scripts? we will discuss it here.
The working of the primary server script is as below.
- Provide the database name in a script for which switchover needs to be performed.
- Once, the database name is provided, the script will check whether the database is configured in log shipping or not. If not, then it will notify the same. If the database is configured in log shipping, then further checks will perform.
- The script will check whether the database is in sync or not. If not, then the script will notify the same and skip further operations.
- Once the sync status is validated and found to be good, the script will check active sessions on a database. If there are any, then it will kill the same.
- Further, the script will fetch the LS_Backup job name and disable the same.
- Once the LS_Backup job is disabled, then the script will execute that job to ensure data must be backed up till that point.
- If in case LS_Backup job is running, the script will wait for its completion.
- Further, the script will take tail log backup in the folder which is configured for log backup on the primary server and get the database in STAND_BY mode. Before taking the tail log backup, the script will ensure the LS_Backup job is not running.
- Once the tail log backup is taken, the script will print the last log backup date and time. We have to match the same for the secondary database.
- I would recommend to save the output after the execution of the script.
- Script for the secondary server is nothing but executing LS_Copy and LS_Restore job. But it has also a few checks which are given below.
- In the secondary script also have to provide the database name.
- Same as the primary server script, it will check whether the database is configured in log shipping or not. Also, it will ensure that sync status is good.
- Next, the script will get details of LS_Copy and LS_Restore jobs and disable those.
- Once jobs are disabled, the script will change the log shipping restoring mode to STAND_BY.
- Next, LS_Backup and LS_Restore jobs will get executed. If those are running, then the script will wait for its completion.
- The script will print the date and time of the last log restored to a secondary database.
Note, we
can execute the second script multiple times till the last restored date and time are not
matching with primary databases.
Scripts have
been tested for the above scenarios and found to be working fine.
Below is
the code given for the script. You can test the same at your end and provide
your feedback.
Please
note, use the below scripts in production after detailed testing and at your own risk.
Script 1:
To be executed on a Primary Server:
USE [master]
------------------------------------------------------------------------------------
--Take log backup with standby
------------------------------------------------------------------------------------
SET NOCOUNT ON
Declare @BackupPath as varchar(100)
Declare @db_name as varchar(100)
-----------CHANGE HERE --------------------
set @db_name = 'AdventureWorks' -- Exm : AdventureWorks
set @BackupPath=(select backup_directory from
msdb..log_shipping_primary_databases where primary_database=@db_name)
--------------------------------------------
DECLARE @BackupFile as VARCHAR(250)
DECLARE @UndoFile as VARCHAR(250)
DECLARE @SQLSTR AS NVARCHAR(500)
DECLARE @DateTime as VARCHAR(50)
DECLARE @YYYY AS VARCHAR(4)
DECLARE @M AS VARCHAR(2)
DECLARE @D AS VARCHAR(2)
DECLARE @HH AS VARCHAR(2)
DECLARE @MIN AS VARCHAR(2)
DECLARE @PJOB AS NVARCHAR(500)
DECLARE @kill AS varchar(8000) = ''; --kill session for db
DECLARE @DatabaseID AS SMALLINT = DB_ID(@db_name);
DECLARE @SQLKILL AS NVARCHAR(10)
DECLARE @DB_Lst_logbkp AS DATETIME
DECLARE @Curdate AS DATETIME
DECLARE @last_backup_min AS int
DECLARE @backup_threshold AS int
set @Curdate=getdate()
set @last_backup_min=(select DATEDIFF(mi, last_backup_date,@Curdate) from msdb..log_shipping_monitor_primary where primary_database=@db_name)
set @backup_threshold=(select backup_threshold from msdb..log_shipping_monitor_primary where primary_database=@db_name)
if exists (SELECT primary_database As 'Databases_For_Logshipping' FROM msdb..log_shipping_primary_databases where primary_database=@db_name)
begin
if (@last_backup_min>@backup_threshold)
begin
Print 'Log shipping for database ' +@db_name+ ' is not in synced. Kindly get it sync on priority !!!'
Print 'Backup is not performed since, ' +CONVERT( VARCHAR, @last_backup_min, 109)+ ' Mins'
Print 'Switch over activity has been cancelled.'
end
else
begin
---Kill Sessions
Print '*********************Killing sessions of primary database if any*********************'
WHILE EXISTS ( SELECT 1 FROM sys.dm_exec_sessions WHERE database_id = @DatabaseID )
BEGIN;
SET @SQLKILL = (SELECT TOP 1
N'kill ' + CAST(session_id AS NVARCHAR(5)) + ';'
FROM
sys.dm_exec_sessions
WHERE
database_id = @DatabaseID
);
EXEC sys.sp_executesql @SQLKILL;
print 'Sessions '+@SQLKILL+' on '+@db_name
END;
Print '*********************Disabling LS Backup job*********************'
set @PJOB=(
select b.name
from msdb..log_shipping_primary_databases a
join msdb..sysjobs b on b.job_id=a.backup_job_id
where primary_database=@db_name and b.name like 'LSBackup%')
--print @PJOB
EXEC msdb.dbo.sp_update_job @job_name=@PJOB,@enabled = 0
print 'SQL JOB '+@PJOB+' has been disabled'
Print '*********************Executing LS Backup job*********************'
--Execute LS backup Job
BEGIN
WHILE 1 = 1
BEGIN
IF
EXISTS (SELECT 1 FROM msdb.dbo.sysjobactivity b
INNER JOIN msdb.dbo.sysjobs a ON b.job_id = a.job_id
WHERE b.start_execution_date IS NOT NULL
AND b.stop_execution_date IS NULL AND a.name=@PJOB)
BEGIN
WAITFOR DELAY '00:00:10';
CONTINUE;
END;
ELSE
BEGIN
EXEC msdb.dbo.sp_start_job @PJOB ;
print 'SQL JOB '+@PJOB+' has been executed successfully'
BREAK;
END;
END;
END;
--Take tail log backup
Print '*********************Initiating tail log backup*********************'
IF CAST(serverproperty ('ProductVersion') AS VARCHAR(1)) = '8'
BEGIN
--*** CREATING DATE PART
SET @YYYY = DATEPART(YYYY, GETDATE())
SET @M = DATEPART(m, GETDATE())
IF @M BETWEEN 1 AND 9
SET @M = '0'+@M
SET @D = DATEPART(d, GETDATE())
IF @D BETWEEN 1 AND 9
SET @D = '0'+@D
SET @HH = DATEPART(HH, GETDATE())
IF @HH BETWEEN 0 AND 9
SET @HH = '0'+@HH
SET @MIN = DATEPART(MINUTE, GETDATE())
IF @MIN BETWEEN 0 AND 9
SET @MIN = '0'+@MIN
SET @DateTime = @YYYY + @M + @D + @HH + @MIN
SET @BackupFile = @BackupPath+ '\' + @db_name + '_tlog_' + @DateTime
+ '.TRN'
SET @UndoFile = @BackupPath+ '\' + @db_name + '_undo_' + @DateTime + '.dat'
END
ELSE
BEGIN
--*** CREATING DATE PART
SET @YYYY = DATEPART(YYYY, GETUTCDATE())
SET @M = DATEPART(m, GETUTCDATE())
IF @M BETWEEN 1 AND 9
SET @M = '0'+@M
SET @D = DATEPART(d, GETUTCDATE())
IF @D BETWEEN 1 AND 9
SET @D = '0'+@D
SET @HH = DATEPART(HH, GETUTCDATE())
IF @HH BETWEEN 0 AND 9
SET @HH = '0'+@HH
SET @MIN = DATEPART(MINUTE, GETUTCDATE())
IF @MIN BETWEEN 0 AND 9
SET @MIN = '0'+@MIN
SET @DateTime = @YYYY + @M + @D + @HH + @MIN + '00'
SET @BackupFile = @BackupPath+ '\' + @db_name + '_' + @DateTime + '.trn'
SET @UndoFile = @BackupPath+ '\' + @db_name + '_undo_' + @DateTime + '.dat'
END
set @SQLSTR='BACKUP LOG [' + @db_name + '] TO DISK = ''' + @BackupFile +''' WITH STANDBY = ''' + @UndoFile +''' , STATS = 25'
BEGIN
WHILE 1 = 1
BEGIN
IF
EXISTS (SELECT 1 FROM msdb.dbo.sysjobactivity b
INNER JOIN msdb.dbo.sysjobs a ON b.job_id = a.job_id
WHERE b.start_execution_date IS NOT NULL
AND b.stop_execution_date IS NULL AND a.name=@PJOB)
BEGIN
WAITFOR DELAY '00:00:10';
CONTINUE;
END;
ELSE
BEGIN
print @SQLSTR
EXECUTE sp_executesql @SQLSTR
BREAK;
END;
END;
END;
set @DB_Lst_logbkp=(select max(backup_finish_date)
from msdb..backupset
where database_name=@db_name
GROUP BY database_name)
Print '*********************Check date and time of last log backup*********************'
print 'Last log backup date of Database '+@db_name+' is ' +CONVERT(
VARCHAR, @DB_Lst_logbkp, 109)
END
END
ELSE
begin
print 'Database '+@db_name+' is not configured for log shipping.'
print 'Switch over actvity can not be performmed.'
END
Script 2 :
To be executed on a Secondary server:
USE [master]
------------------------------------------------------------------------------------
-- Restore log backup
-- Script can be execute multiple time till data dosnt get sync
------------------------------------------------------------------------------------
SET NOCOUNT ON
Declare @db_name as varchar(100)
-----------CHANGE HERE --------------------
set @db_name = 'AdventureWorks' -- Exm : AdventureWorks
--------------------------------------------
DECLARE @SQLSTR AS NVARCHAR(500)
DECLARE @CopyJOB AS NVARCHAR(500)
DECLARE @RestoreJOB AS NVARCHAR(500)
DECLARE @DB_Lst_logbkp AS datetime
DECLARE @last_restored_file AS nvarchar(1000)
DECLARE @Curdate AS DATETIME
DECLARE @last_restored_min AS int
DECLARE @restore_threshold AS int
set @Curdate=getdate()
set @Curdate=getdate()
set @last_restored_min=(select DATEDIFF(mi, last_restored_date,@Curdate) from msdb..log_shipping_monitor_secondary where secondary_database=@db_name)
set @restore_threshold=(select restore_threshold from msdb..log_shipping_monitor_secondary where secondary_database=@db_name)
if exists (SELECT secondary_database As 'Databases_For_Logshipping' FROM msdb..log_shipping_secondary_databases where secondary_database=@db_name)
begin
if (@last_restored_min>@restore_threshold)
begin
Print 'Log shipping for database ' +@db_name+ ' is not in synced. Kindly get it sync on priority !!!'
Print 'Log is not restored from ' +CONVERT( VARCHAR, @last_restored_min, 109)+ ' Mins'
Print 'Switch back activity has been cancelled.'
END;
else
begin
--Disable LS backup Job
Print '*********************Disabling LS Copy/Restore job*********************'
set @CopyJOB=(
select b.name
from msdb..log_shipping_secondary a
join msdb..sysjobs b on b.job_id=a.copy_job_id
where a.primary_database=@db_name and b.name like 'LScopy%')
--print @CopyJOB
set @RestoreJOB=(
select b.name
from msdb..log_shipping_secondary a
join msdb..sysjobs b on b.job_id=a.restore_job_id
where a.primary_database=@db_name and b.name like 'LSRestore%')
--print @RestoreJOB
EXEC msdb.dbo.sp_update_job @job_name=@CopyJOB,@enabled = 0
EXEC msdb.dbo.sp_update_job @job_name=@RestoreJOB,@enabled = 0
print 'SQL JOB '+@CopyJOB+' has been disabled'
print 'SQL JOB '+@RestoreJOB+' has been disabled'
--Get Restoration mode in standby
Print '*********************Changing restoration mode to STAND BY*********************'
Begin
EXEC sp_change_log_shipping_secondary_database
@secondary_database =@db_name,
@restore_delay = 0,
@restore_all = 1,
@restore_mode = 1,
@disconnect_users = 1
print 'Restoration mode of '+@db_name+' has been changed to STANDBY MODE'
END;
Print '*********************Executing LS Copy/restore job*********************'
BEGIN
WHILE 1 = 1
BEGIN
IF
EXISTS (SELECT 1 FROM msdb.dbo.sysjobactivity b
INNER JOIN msdb.dbo.sysjobs a ON b.job_id = a.job_id
WHERE b.start_execution_date IS NOT NULL
AND b.stop_execution_date IS NULL AND a.name=@CopyJOB)
BEGIN
WAITFOR DELAY '00:00:10';
CONTINUE;
END;
ELSE
BEGIN
EXEC msdb.dbo.sp_start_job @CopyJOB ;
print 'SQL JOB '+@CopyJOB+' has been executed successfully'
BREAK;
END;
END;
END;
BEGIN
WHILE 1 = 1
BEGIN
IF
EXISTS (SELECT 1 FROM msdb.dbo.sysjobactivity b
INNER JOIN msdb.dbo.sysjobs a ON b.job_id = a.job_id
WHERE b.start_execution_date IS NOT NULL
AND b.stop_execution_date IS NULL AND a.name=@RestoreJOB)
BEGIN
WAITFOR DELAY '00:00:10';
CONTINUE;
END;
ELSE
BEGIN
EXEC msdb.dbo.sp_start_job @RestoreJOB ;
print 'SQL JOB '+@CopyJOB+' has been executed successfully'
BREAK;
END;
END;
END;
set @DB_Lst_logbkp=(select max(backup_finish_date)
from msdb..backupset
where database_name=@db_name
GROUP BY database_name)
Print '*********************Check date and time of last log backup*********************'
print 'Last log backup date of Database '+@db_name+' is ' +CONVERT(
VARCHAR, @DB_Lst_logbkp, 109)
END;
END;
ELSE
begin
print 'Database '+@db_name+' is not configured for log shipping.'
print 'Switch back actvity can not be performmed.'
END;
Thanks for this blog its really helpful👍
ReplyDelete