web-dev-qa-db-ja.com

現在のサーバーが可用性グループに含まれているかどうか、および含まれている場合は、それがプライマリかどうかを確認する機能

以前は可用性グループに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 
3

私たちは分散型可用性グループ(およびまっすぐな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);
3
Jonathan Fite

以下のスクリプトでは、以下の状況で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
0