Allways on Availability Group

Here i am trying to explain most common internal objects to get information of database with always on.

Following query provides good overview of Always On availability groups health and status.

SELECT ag.name AS [AG Name]
	,ar.replica_server_name
	,ar.availability_mode_desc
	,adc.[database_name]
	,drs.is_local
	,drs.is_primary_replica
	,drs.synchronization_state_desc
	,drs.is_commit_participant
	,drs.synchronization_health_desc
	,drs.recovery_lsn
	,drs.truncation_lsn
	,drs.last_sent_lsn
	,drs.last_sent_time
	,drs.last_received_lsn
	,drs.last_received_time
	,drs.last_hardened_lsn
	,drs.last_hardened_time
	,drs.last_redone_lsn
	,drs.last_redone_time
	,drs.log_send_queue_size
	,drs.log_send_rate
	,drs.redo_queue_size
	,drs.redo_rate
	,drs.filestream_send_rate
	,drs.end_of_log_lsn
	,drs.last_commit_lsn
	,drs.last_commit_time
	,drs.database_state_desc
FROM sys.dm_hadr_database_replica_states AS drs WITH (NOLOCK)
INNER JOIN sys.availability_databases_cluster AS adc WITH (NOLOCK) ON drs.group_id = adc.group_id
	AND drs.group_database_id = adc.group_database_id
INNER JOIN sys.availability_groups AS ag WITH (NOLOCK) ON ag.group_id = drs.group_id
INNER JOIN sys.availability_replicas AS ar WITH (NOLOCK) ON drs.group_id = ar.group_id
	AND drs.replica_id = ar.replica_id
ORDER BY ag.name
	,ar.replica_server_name
	,adc.[database_name]
OPTION (RECOMPILE);

Show All availability groups visible to this server where this Server is a Secondary replica

SELECT Groups.[Name] AS AGname
	,primary_replica
FROM sys.dm_hadr_availability_group_states States
INNER JOIN master.sys.availability_groups Groups ON States.group_id = Groups.group_id
WHERE primary_replica != @@Servername;

Show All Databases in an availability group visible to this server where this Server is the primary replica

SELECT Groups.[Name] AS AGname
	,AGD.database_name
FROM sys.dm_hadr_availability_group_states States
INNER JOIN master.sys.availability_groups Groups ON States.group_id = Groups.group_id
INNER JOIN sys.availability_databases_cluster AGD ON Groups.group_id = AGD.group_id
WHERE primary_replica = @@Servername
ORDER BY AGname ASC
	,database_name ASC;

Show Availability groups visible to the Server and Replica information such as Which server is the Primary
Sync and Async modes , Readable Secondary and Failover Mode, these can all be filtered using a Where clause
if you are running some checks, no Where clause will show you all of the information.

WITH AGStatus
AS (
	SELECT name AS AGname
		,replica_server_name
		,CASE 
			WHEN (primary_replica = replica_server_name)
				THEN 1
			ELSE ''
			END AS IsPrimaryServer
		,secondary_role_allow_connections_desc AS ReadableSecondary
		,[availability_mode] AS [Synchronous]
		,failover_mode_desc
	FROM master.sys.availability_groups Groups
	INNER JOIN master.sys.availability_replicas Replicas ON Groups.group_id = Replicas.group_id
	INNER JOIN master.sys.dm_hadr_availability_group_states States ON Groups.group_id = States.group_id
	)
SELECT [AGname]
	,[Replica_server_name]
	,[IsPrimaryServer]
	,[Synchronous]
	,[ReadableSecondary]
	,[Failover_mode_desc]
FROM AGStatus
--WHERE IsPrimaryServer = 1 AND Synchronous = 1
ORDER BY AGname ASC
	,IsPrimaryServer DESC;

All status AG Availability Group

--->> all status AG Avaibility Group
SELECT [AG].[name] AS [AvailabilityGroupName]
	,[AR].[replica_server_name] AS [AvailabilityReplicaServerName]
	,[dbcs].[database_name] AS [AvailabilityDatabaseName]
	,ISNULL([dbcs].[is_failover_ready], 0) AS [IsFailoverReady]
	,ISNULL([arstates].[role_desc], 3) AS [ReplicaRole]
	,[AR].[availability_mode_desc] AS [AvailabilityMode]
	,CASE [dbcs].[is_failover_ready]
		WHEN 1
			THEN 0
		ELSE ISNULL(DATEDIFF([ss], [dbr].[last_commit_time], [dbrp].[last_commit_time]), 0)
		END AS [EstimatedDataLoss_(Seconds)]
	,ISNULL(CASE [dbr].[redo_rate]
			WHEN 0
				THEN - 1
			ELSE CAST([dbr].[redo_queue_size] AS FLOAT) / [dbr].[redo_rate]
			END, - 1) AS [EstimatedRecoveryTime_(Seconds)]
	,ISNULL([dbr].[is_suspended], 0) AS [IsSuspended]
	,ISNULL([dbr].[suspend_reason_desc], '-') AS [SuspendReason]
	,ISNULL([dbr].[synchronization_state_desc], 0) AS [SynchronizationState]
	,ISNULL([dbr].[last_received_time], 0) AS [LastReceivedTime]
	,ISNULL([dbr].[last_redone_time], 0) AS [LastRedoneTime]
	,ISNULL([dbr].[last_sent_time], 0) AS [LastSentTime]
	,ISNULL([dbr].[log_send_queue_size], - 1) AS [LogSendQueueSize]
	,ISNULL([dbr].[log_send_rate], - 1) AS [LogSendRate_KB/S]
	,ISNULL([dbr].[redo_queue_size], - 1) AS [RedoQueueSize_KB]
	,ISNULL([dbr].[redo_rate], - 1) AS [RedoRate_KB/S]
	,ISNULL(CASE [dbr].[log_send_rate]
			WHEN 0
				THEN - 1
			ELSE CAST([dbr].[log_send_queue_size] AS FLOAT) / [dbr].[log_send_rate]
			END, - 1) AS [SynchronizationPerformance]
	,ISNULL([dbr].[filestream_send_rate], - 1) AS [FileStreamSendRate]
	,ISNULL([dbcs].[is_database_joined], 0) AS [IsJoined]
	,[arstates].[is_local] AS [IsLocal]
	,ISNULL([dbr].[last_commit_lsn], 0) AS [LastCommitLSN]
	,ISNULL([dbr].[last_commit_time], 0) AS [LastCommitTime]
	,ISNULL([dbr].[last_hardened_lsn], 0) AS [LastHardenedLSN]
	,ISNULL([dbr].[last_hardened_time], 0) AS [LastHardenedTime]
	,ISNULL([dbr].[last_received_lsn], 0) AS [LastReceivedLSN]
	,ISNULL([dbr].[last_redone_lsn], 0) AS [LastRedoneLSN]
FROM SYS.AVAILABILITY_GROUPS AS [AG]
INNER JOIN sys.availability_replicas AS [AR] ON [AR].[group_id] = [AG].[group_id]
INNER JOIN sys.dm_hadr_database_replica_cluster_states AS [dbcs] ON [dbcs].[replica_id] = [AR].[replica_id]
LEFT OUTER JOIN sys.dm_hadr_database_replica_states AS [dbr] ON [dbcs].[replica_id] = [dbr].[replica_id]
	AND [dbcs].[group_database_id] = [dbr].[group_database_id]
LEFT OUTER JOIN (
	SELECT [ars].[role]
		,[drs].[database_id]
		,[drs].[replica_id]
		,[drs].[last_commit_time]
	FROM sys.dm_hadr_database_replica_states AS [drs]
	LEFT JOIN sys.dm_hadr_availability_replica_states [ars] ON [drs].[replica_id] = [ars].[replica_id]
	WHERE [ars].[role] = 1
	) AS [dbrp] ON [dbr].[database_id] = [dbrp].[database_id]
INNER JOIN sys.dm_hadr_availability_replica_states AS [arstates] ON [arstates].[replica_id] = [AR].[replica_id]
--WHERE  [AG].[name] = ISNULL(@AGname,[AG].[name])
ORDER BY [AvailabilityReplicaServerName] ASC
	,[AvailabilityDatabaseName] ASC;

prtfromance and estimation

SELECT [AG].[name] AS [AvailabilityGroupName]
	,[AR].[replica_server_name] AS [AvailabilityReplicaServerName]
	,[dbcs].[database_name] AS [AvailabilityDatabaseName]
	,ISNULL([dbcs].[is_failover_ready], 0) AS [IsFailoverReady]
	,ISNULL([arstates].[role_desc], 3) AS [ReplicaRole]
	,CASE [dbcs].[is_failover_ready]
		WHEN 1
			THEN 0
		ELSE ISNULL(DATEDIFF([ss], [dbr].[last_commit_time], [dbrp].[last_commit_time]), 0)
		END AS [EstimatedDataLoss_(Seconds)]
	,ISNULL(CASE [dbr].[redo_rate]
			WHEN 0
				THEN - 1
			ELSE CAST([dbr].[redo_queue_size] AS FLOAT) / [dbr].[redo_rate]
			END, - 1) AS [EstimatedRecoveryTime_(Seconds)]
	,ISNULL(CASE [dbr].[log_send_rate]
			WHEN 0
				THEN - 1
			ELSE CAST([dbr].[log_send_queue_size] AS FLOAT) / [dbr].[log_send_rate]
			END, - 1) AS [SynchronizationPerformance]
	,ISNULL([dbr].[filestream_send_rate], - 1) AS [FileStreamSendRate]
	,ISNULL([dbcs].[is_database_joined], 0) AS [IsJoined]
	,[arstates].[is_local] AS [IsLocal]
FROM SYS.AVAILABILITY_GROUPS AS [AG]
INNER JOIN sys.availability_replicas AS [AR] ON [AR].[group_id] = [AG].[group_id]
INNER JOIN sys.dm_hadr_database_replica_cluster_states AS [dbcs] ON [dbcs].[replica_id] = [AR].[replica_id]
LEFT OUTER JOIN sys.dm_hadr_database_replica_states AS [dbr] ON [dbcs].[replica_id] = [dbr].[replica_id]
	AND [dbcs].[group_database_id] = [dbr].[group_database_id]
LEFT OUTER JOIN (
	SELECT [ars].[role]
		,[drs].[database_id]
		,[drs].[replica_id]
		,[drs].[last_commit_time]
	FROM sys.dm_hadr_database_replica_states AS [drs]
	LEFT JOIN sys.dm_hadr_availability_replica_states [ars] ON [drs].[replica_id] = [ars].[replica_id]
	WHERE [ars].[role] = 1
	) AS [dbrp] ON [dbr].[database_id] = [dbrp].[database_id]
INNER JOIN sys.dm_hadr_availability_replica_states AS [arstates] ON [arstates].[replica_id] = [AR].[replica_id]
--WHERE  [AG].[name] = ISNULL(@AGname,[AG].[name])
ORDER BY [AvailabilityDatabaseName] ASC;

AG log read

sp_readerrorlog 0,1,'The state of the local availability replica',''
sp_readerrorlog 0,1, 'RESOLVING'

SQL Server internal objects

SELECT member_name
	,member_state_desc
	,number_of_quorum_votes
	,*
FROM sys.dm_hadr_cluster_members;

SELECT cluster_name
	,quorum_type_desc
	,quorum_state_desc
	,*
FROM sys.dm_hadr_cluster WITH (NOLOCK)
OPTION (RECOMPILE);

SELECT *
FROM sys.dm_hadr_cluster_networks
select * from sys.dm_hadr_auto_page_repair
select * from sys.dm_hadr_cluster_networks
select * from sys.dm_hadr_availability_group_states
select * from sys.dm_hadr_database_replica_cluster_states
select * from sys.dm_hadr_availability_replica_cluster_nodes
select * from sys.dm_hadr_database_replica_states
select * from sys.dm_hadr_availability_replica_cluster_states
select * from sys.dm_hadr_instance_node_map
select * from sys.dm_hadr_availability_replica_states
select * from sys.dm_hadr_name_id_map
select * from sys.dm_hadr_cluster
select * from sys.dm_tcp_listener_states
select * from sys.dm_hadr_cluster_members
-- Availability Groups catalog views 
select * from sys.availability_databases_cluster
select * from sys.availability_groups_cluster
select * from sys.availability_group_listener_ip_addresses
select * from sys.availability_read_only_routing_lists
select * from sys.availability_group_listeners
select * from sys.availability_replicas
select * from sys.availability_groups

SQL Server extended event review using script

SELECT  xml_data.value('(event/@name)[1]','varchar(max)') AS 'Name'  
,xml_data.value('(event/@package)[1]','varchar(max)') AS 'Package'  
,xml_data.value('(event/@timestamp)[1]','datetime') AS 'Time'  
,xml_data.value('(event/data[@name=''state'']/value)[1]','int') AS 'State'  
,xml_data.value('(event/data[@name=''state_desc'']/text)[1]','varchar(max)') AS 'State Description'  
,xml_data.value('(event/data[@name=''failure_condition_level'']/value)[1]','int') AS 'Failure Conditions'  
,xml_data.value('(event/data[@name=''node_name'']/value)[1]','varchar(max)') AS 'Node_Name'  
,xml_data.value('(event/data[@name=''instancename'']/value)[1]','varchar(max)') AS 'Instance Name'  
,xml_data.value('(event/data[@name=''creation time'']/value)[1]','datetime') AS 'Creation Time'  
,xml_data.value('(event/data[@name=''component'']/value)[1]','varchar(max)') AS 'Component'  
,xml_data.value('(event/data[@name=''message'']/value)[1]','varchar(max)') AS 'message'  
,xml_data.value('(event/data[@name=''destination'']/text)[1]','varchar(max)') AS 'destination' 
,xml_data.value('(event/data[@name=''availability_group_name'']/value)[1]','varchar(max)') AS 'availability_group_name' 
,xml_data.value('(event/data[@name=''current_state'']/text)[1]','varchar(max)') AS current_state
,xml_data.value('(event/data[@name=''previous_state'']/text)[1]','varchar(max)') AS previous_state
,xml_data.value('(event/data[@name=''data'']/value)[1]','varchar(max)') AS 'Data'  
,xml_data.value('(event/data[@name=''info'']/value)[1]','varchar(max)') AS 'Info'  ,xml_data
FROM   ( SELECT object_name AS 'event'    ,CONVERT(xml,event_data) AS 'xml_data' FROM sys.fn_xe_file_target_read_file('AlwaysOn*.xel',NULL,NULL,NULL)   
)   AS XEventData  
ORDER BY Time;  

;WITH cte_HADR
AS (
	SELECT object_name
		,CONVERT(XML, event_data) AS data
	FROM sys.fn_xe_file_target_read_file('AlwaysOn*.xel', NULL, NULL, NULL)
	WHERE object_name = 'error_reported'
	)
SELECT data.value('(/event/@timestamp)[1]', 'datetime') AS [timestamp]
	,data.value('(/event/data[@name=''error_number''])[1]', 'int') AS [error_number]
	,data.value('(/event/data[@name=''message''])[1]', 'varchar(max)') AS [message]
FROM cte_HADR
WHERE data.value('(/event/data[@name=''error_number''])[1]', 'int') = 1480

uspCalculateRPO
Calculate RPO of a secondary database.
@group_id uniqueidentifier: group_id of the secondary database.
@replica_id uniqueidentifier: replica_id of the secondary database.
@group_database_id uniqueidentifier: group_database_id of the secondary database.

CREATE PROCEDURE uspCalculateRPO (
	@group_id UNIQUEIDENTIFIER
	,@replica_id UNIQUEIDENTIFIER
	,@group_database_id UNIQUEIDENTIFIER
	)
AS
BEGIN
	DECLARE @db_name SYSNAME
	DECLARE @is_primary_replica BIT
	DECLARE @is_failover_ready BIT
	DECLARE @is_local BIT
	DECLARE @last_commit_time_sec DATETIME
	DECLARE @last_commit_time_pri DATETIME
	DECLARE @RPO NVARCHAR(max)

	-- secondary database's last_commit_time 
	SELECT @db_name = dbcs.database_name
		,@is_failover_ready = dbcs.is_failover_ready
		,@last_commit_time_sec = dbr.last_commit_time
	FROM sys.dm_hadr_database_replica_states dbr
	JOIN sys.dm_hadr_database_replica_cluster_states dbcs ON dbr.replica_id = dbcs.replica_id
		AND dbr.group_database_id = dbcs.group_database_id
	WHERE dbr.group_id = @group_id
		AND dbr.replica_id = @replica_id
		AND dbr.group_database_id = @group_database_id

	-- correlated primary database's last_commit_time 
	SELECT @last_commit_time_pri = dbr.last_commit_time
		,@is_local = dbr.is_local
	FROM sys.dm_hadr_database_replica_states dbr
	JOIN sys.dm_hadr_database_replica_cluster_states dbcs ON dbr.replica_id = dbcs.replica_id
		AND dbr.group_database_id = dbcs.group_database_id
	WHERE dbr.group_id = @group_id
		AND dbr.is_primary_replica = 1
		AND dbr.group_database_id = @group_database_id

	IF @is_local IS NULL
		OR @is_failover_ready IS NULL
	BEGIN
		PRINT 'RPO of database ' + @db_name + ' is not available'

		RETURN
	END

	IF @is_local = 0
	BEGIN
		PRINT 'You are visiting wrong replica'

		RETURN
	END

	IF @is_failover_ready = 1
		SET @RPO = '00:00:00'
	ELSE IF @last_commit_time_sec IS NULL
		OR @last_commit_time_pri IS NULL
	BEGIN
		PRINT 'RPO of database ' + @db_name + ' is not available'

		RETURN
	END
	ELSE
	BEGIN
		IF DATEDIFF(ss, @last_commit_time_sec, @last_commit_time_pri) < 0
		BEGIN
			PRINT 'RPO of database ' + @db_name + ' is not available'

			RETURN
		END
		ELSE
			SET @RPO = CONVERT(VARCHAR, DATEADD(ms, datediff(ss, @last_commit_time_sec, @last_commit_time_pri) * 1000, 0), 114)
	END

	PRINT 'RPO of database ' + @db_name + ' is ' + @RPO
END

uspCalculateRTO:- Calculate RTO of a secondary database

parameters: @secondary_database_name nvarchar(max): name of the secondary database.

CREATE PROCEDURE uspCalculateRTO (@secondary_database_name NVARCHAR(max))
AS
BEGIN
	DECLARE @db SYSNAME
	DECLARE @is_primary_replica BIT
	DECLARE @is_failover_ready BIT
	DECLARE @redo_queue_size BIGINT
	DECLARE @redo_rate BIGINT
	DECLARE @replica_id UNIQUEIDENTIFIER
	DECLARE @group_database_id UNIQUEIDENTIFIER
	DECLARE @group_id UNIQUEIDENTIFIER
	DECLARE @RTO FLOAT

	SELECT @is_primary_replica = dbr.is_primary_replica
		,@is_failover_ready = dbcs.is_failover_ready
		,@redo_queue_size = dbr.redo_queue_size
		,@redo_rate = dbr.redo_rate
		,@replica_id = dbr.replica_id
		,@group_database_id = dbr.group_database_id
		,@group_id = dbr.group_id
	FROM sys.dm_hadr_database_replica_states dbr
	JOIN sys.dm_hadr_database_replica_cluster_states dbcs ON dbr.replica_id = dbcs.replica_id
		AND dbr.group_database_id = dbcs.group_database_id
	WHERE dbcs.database_name = @secondary_database_name

	IF @is_primary_replica IS NULL
		OR @is_failover_ready IS NULL
		OR @redo_queue_size IS NULL
		OR @replica_id IS NULL
		OR @group_database_id IS NULL
		OR @group_id IS NULL
	BEGIN
		PRINT 'RTO of Database ' + @secondary_database_name + ' is not available'

		RETURN
	END
	ELSE IF @is_primary_replica = 1
	BEGIN
		PRINT 'You are visiting wrong replica';

		RETURN
	END

	IF @redo_queue_size = 0
		SET @RTO = 0
	ELSE IF @redo_rate IS NULL
		OR @redo_rate = 0
	BEGIN
		PRINT 'RTO of Database ' + @secondary_database_name + ' is not available'

		RETURN
	END
	ELSE
		SET @RTO = CAST(@redo_queue_size AS FLOAT) / @redo_rate

	PRINT 'RTO of Database ' + @secondary_database_name + ' is ' + convert(VARCHAR, ceiling(@RTO))
	PRINT 'group_id of Database ' + @secondary_database_name + ' is ' + convert(NVARCHAR(50), @group_id)
	PRINT 'replica_id of Database ' + @secondary_database_name + ' is ' + convert(NVARCHAR(50), @replica_id)
	PRINT 'group_database_id of Database ' + @secondary_database_name + ' is ' + convert(NVARCHAR(50), @group_database_id)
END

RAISERROR (
		'creating procedure proc_calculate_RPO'
		,0
		,1
		)
WITH NOWAIT
GO


Spread the love

Leave a Comment