以前は可用性グループに2台のサーバーがありましたが、パッチ適用中にそれらのサーバーの1台に問題があり、長い話を短くしました 私はすべてのデータベースを可用性グループから削除しました 。
プライマリサーバーでのみジョブの実行を管理する方法 は、主に データベースがプライマリレプリカにあるかどうかをテストするこのスクリプトによるものです :
If sys.fn_hadr_is_primary_replica ('apcore') =1
BEGIN
EXEC [APCore].[app].[usp_upd_applicationStatusTimeExpired]
END
しかし、現在のサーバーは可用性グループ内の唯一のサーバーなので、このスクリプトは
select sys.fn_hadr_is_primary_replica ('apcore')
nullを返すため、正確ではありません。
次のいずれかの場合にbit
1を返す関数を開発しています。
1-私たちは可用性グループの一部ではありません
2-私たちは可用性グループの一部であり、私たちはプライマリサーバーです
質問は:
この関数は 分散型可用性グループ に対して機能しますか?この関数が期待値を返すのを妨げる可能性があると私が考えていなかった他の状況はありますか?
そしてここに関数があります:関数はジョブ内で使用され、ジョブを実行する必要があるかどうかを確認します(私たちは可用性グループに属していて、私たちはプライマリーであるか、または可用性グループにまったく属していません)。
--=========================================
-- scalar-valued function dbo.check_HADR_status
-- returns 1 when either primary or we are standalone
-- (not part of an availability group)
-- USAGE:
-- SELECT MASTER.dbo.check_HADR_status()
--=========================================
USE MASTER
GO
IF OBJECT_ID (N'dbo.check_HADR_status') IS NOT NULL
DROP FUNCTION dbo.check_HADR_status
GO
CREATE FUNCTION dbo.check_HADR_status()
RETURNS BIT
WITH EXECUTE AS CALLER
AS
BEGIN
RETURN (SELECT CASE WHEN EXISTS (select * from sys.availability_replicas) THEN
CASE WHEN ( SELECT COALESCE(a.role_desc,'RADHE')
FROM sys.dm_hadr_availability_replica_states AS a
JOIN sys.availability_replicas AS b
ON b.replica_id = a.replica_id
WHERE b.replica_server_name = @@ServerName) LIKE 'PRIMARY' THEN 1
ELSE 0
END
ELSE 1
END)
END
GO
IF (SELECT MASTER.dbo.check_HADR_status()) = 1
BEGIN
PRINT 'RUN THE JOB STEP'
END
ELSE
BEGIN
PRINT 'DON''T RUN THE JOB AS WE ARE IN A SECONDARY REPLICA'
END
私たちは分散型可用性グループ(およびまっすぐなAGにある一部のサーバー)を使用していて、同様の問題に遭遇しました。最終的には、このクエリを最終的な解決策として解決しました。おそらく必要以上に複雑ですが、機能します。
DECLARE @DBName sysname;
DECLARE @IsPrimary BIT = 0;
--Determine if the database selected is online.
;WITH CTE_DAG
AS
( SELECT AG.[name] AS DAGName
, AG.is_distributed
, AR.replica_server_name AS UnderlyingAG
, ARS.role_desc
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_availability_replica_states AS ARS ON ARS.replica_id = AR.replica_id
WHERE AG.is_distributed = 1)
, CTE_LocalAG
AS
( SELECT AG.[name] AS LocalAGName
, AG.is_distributed
, AR.replica_server_name AS UnderlyingAG
, ARS.role_desc
, D.[name] AS DatabaseName
, DRS.is_primary_replica
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_availability_replica_states AS ARS ON ARS.replica_id = AR.replica_id
INNER JOIN sys.dm_hadr_availability_replica_cluster_states AS ARCS ON ARCS.group_id = AG.group_id
LEFT OUTER JOIN sys.dm_hadr_database_replica_states AS DRS ON DRS.replica_id = ARCS.replica_id
AND DRS.group_id = ARCS.group_id
INNER JOIN sys.databases AS D ON D.database_id = DRS.database_id
WHERE AG.is_distributed = 0
AND ARCS.replica_server_name = @@SERVERNAME)
, CTE_Composite
AS
( SELECT L.DatabaseName
, L.role_desc
, L.is_primary_replica
, COALESCE(D.role_desc, 'NONE') AS DAG_Role
, IsAllPrimary = CASE WHEN L.is_primary_replica = 1
AND COALESCE(D.role_desc, 'NONE') IN ('NONE', 'PRIMARY')
THEN 1
ELSE 0
END
FROM CTE_LocalAG AS L
LEFT OUTER JOIN CTE_DAG AS D ON D.UnderlyingAG = L.LocalAGName
WHERE L.DatabaseName = @DBName)
, CTE_Grouping
AS
( SELECT DatabaseName
, SUM(IsAllPrimary) AS TotalPrimary
, COUNT(DatabaseName) AS TotalCount
FROM CTE_Composite
GROUP BY DatabaseName)
SELECT TOP (1)
@IsPrimary = 1
FROM CTE_Grouping
WHERE COALESCE(TotalPrimary, 0) = COALESCE(TotalCount, 0);
以下のスクリプトでは、以下の状況でyes, it is the primary server
を返すために、若干の変更を加えた Jonathan Fiteのスクリプト を使用しています。
1)可用性グループがない場合-したがって、現在のサーバーがプライマリです
2)可用性グループはありますが、現在のサーバーのみがその一部です-したがって、これはプライマリサーバーです
分散アベイラビリティグループ環境では、まだ入手していないため、テストできませんでした。
私は元のロジックを維持したいと思い、パラメーター@DBNameがnullの場合にのみ追加を追加しました。
すべての変更を%%
でマークしました
DECLARE @DBName sysname;
DECLARE @IsPrimary BIT = 0;
--Determine if the database selected is online.
;WITH CTE_DAG
AS
(
SELECT AG.[name] AS DAGName
, AG.is_distributed
, AR.replica_server_name AS UnderlyingAG
, ARS.role_desc
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_availability_replica_states AS ARS ON ARS.replica_id = AR.replica_id
WHERE AG.is_distributed = 1
)
, CTE_LocalAG
AS
(
SELECT AG.[name] AS LocalAGName
, AG.is_distributed
, AR.replica_server_name AS UnderlyingAG
, ARS.role_desc
, D.[name] AS DatabaseName
, is_primary_replica=CASE WHEN ARS.role_desc = 'PRIMARY' THEN 1 ELSE DRS.is_primary_replica END --%% -- changed to accomodate the situation when one single server in the availability group
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_availability_replica_states AS ARS ON ARS.replica_id = AR.replica_id
INNER JOIN sys.dm_hadr_availability_replica_cluster_states AS ARCS ON ARCS.group_id = AG.group_id
LEFT OUTER JOIN sys.dm_hadr_database_replica_states AS DRS ON DRS.replica_id = ARCS.replica_id
AND DRS.group_id = ARCS.group_id
LEFT OUTER JOIN sys.databases AS D ON D.database_id = DRS.database_id --%% -- changed to LEFT OUTER JOIN because databases may not be in the availability group
WHERE AG.is_distributed = 0
AND ARCS.replica_server_name = @@SERVERNAME
UNION ALL
--this will return true ONLY if there is no availability group at all
--in this case we are the primary
--%%
SELECT @@SERVERNAME AS LocalAGName
, 0 as is_distributed
, NULL AS UnderlyingAG
, 'PRIMARY' as role_desc
, NULL AS DatabaseName
, 1 as is_primary_replica
FROM (VALUES (1)) AS X(A)
WHERE NOT EXISTS (select * from sys.availability_replicas)
)
, CTE_Composite
AS
(
SELECT L.DatabaseName
, L.role_desc
, L.is_primary_replica
, COALESCE(D.role_desc, 'NONE') AS DAG_Role
, IsAllPrimary = CASE WHEN L.is_primary_replica = 1
AND COALESCE(D.role_desc, 'NONE') IN ('NONE', 'PRIMARY')
THEN 1
ELSE 0
END
FROM CTE_LocalAG AS L
LEFT OUTER JOIN CTE_DAG AS D ON D.UnderlyingAG = L.LocalAGName
WHERE L.DatabaseName = @DBName OR (@DBName IS NULL) --%% added the null option for @DBNAME
)
, CTE_Grouping
AS
(
SELECT DatabaseName
, SUM(IsAllPrimary) AS TotalPrimary
, COUNT( case when @DBName IS NULL then 1 else DatabaseName end) AS TotalCount
FROM CTE_Composite
GROUP BY DatabaseName
)
SELECT TOP (1)
@IsPrimary = 1
FROM CTE_Grouping
WHERE COALESCE(TotalPrimary, 0) = COALESCE(TotalCount, 0);
SELECT @IsPrimary