The availability group collector also reads sys.dm_hadr_cluster_members (empty or unavailable without a WSFC), and SQLI-AG-003 (High) reports a node or witness that is not up, with how many members are still up. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
95 lines
3.4 KiB
C#
95 lines
3.4 KiB
C#
namespace SQLInsight.Core.Data;
|
|
|
|
/// <summary>
|
|
/// Always On availability group state as seen from this instance. A primary sees all replicas; a secondary only itself.
|
|
/// </summary>
|
|
public sealed record AvailabilityGroupSnapshot
|
|
{
|
|
public required DateTimeOffset CapturedAt { get; init; }
|
|
public required IReadOnlyList<ReplicaState> Replicas { get; init; }
|
|
public required IReadOnlyList<DatabaseReplicaState> Databases { get; init; }
|
|
|
|
/// <summary>The Windows cluster's nodes and witness (sys.dm_hadr_cluster_members); empty without a WSFC.</summary>
|
|
public IReadOnlyList<ClusterMember> ClusterMembers { get; init; } = [];
|
|
}
|
|
|
|
public sealed record ReplicaState
|
|
{
|
|
public required string Group { get; init; }
|
|
public required string Replica { get; init; }
|
|
public bool IsLocal { get; init; }
|
|
|
|
/// <summary>PRIMARY, SECONDARY or RESOLVING; null when this instance cannot see the replica's state.</summary>
|
|
public string? Role { get; init; }
|
|
|
|
public string? ConnectedState { get; init; }
|
|
|
|
/// <summary>HEALTHY, PARTIALLY_HEALTHY or NOT_HEALTHY.</summary>
|
|
public string? SynchronizationHealth { get; init; }
|
|
|
|
public string? AvailabilityMode { get; init; }
|
|
public string? FailoverMode { get; init; }
|
|
}
|
|
|
|
public sealed record DatabaseReplicaState
|
|
{
|
|
public required string Group { get; init; }
|
|
public required string Database { get; init; }
|
|
public required string Replica { get; init; }
|
|
public bool IsLocal { get; init; }
|
|
public bool IsPrimaryReplica { get; init; }
|
|
public string? SynchronizationState { get; init; }
|
|
public string? SynchronizationHealth { get; init; }
|
|
public bool IsSuspended { get; init; }
|
|
public string? SuspendReason { get; init; }
|
|
|
|
/// <summary>Log not yet sent to this secondary, in KB.</summary>
|
|
public long? LogSendQueueKb { get; init; }
|
|
|
|
public long? LogSendRateKbPerSecond { get; init; }
|
|
|
|
/// <summary>Log received but not yet redone on this secondary, in KB.</summary>
|
|
public long? RedoQueueKb { get; init; }
|
|
|
|
public long? RedoRateKbPerSecond { get; init; }
|
|
public DateTime? LastCommitTime { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// SQL Server Agent: whether it runs, and the latest outcome of each job.
|
|
/// </summary>
|
|
public sealed record AgentJobSnapshot
|
|
{
|
|
public required DateTimeOffset CapturedAt { get; init; }
|
|
|
|
/// <summary>False when no SQL Agent session exists (the service is stopped).</summary>
|
|
public required bool IsAgentRunning { get; init; }
|
|
|
|
public required IReadOnlyList<AgentJob> Jobs { get; init; }
|
|
}
|
|
|
|
public sealed record AgentJob
|
|
{
|
|
public required string Name { get; init; }
|
|
public bool IsEnabled { get; init; }
|
|
|
|
/// <summary>0 failed, 1 succeeded, 2 retry, 3 canceled, 4 in progress; null if never run.</summary>
|
|
public int? LastRunStatus { get; init; }
|
|
|
|
public DateTime? LastRunAt { get; init; }
|
|
|
|
/// <summary>The message of the most recent failed step, which says why (the job-level message does not).</summary>
|
|
public string? LastFailedStepMessage { get; init; }
|
|
|
|
public string? LastFailedStepName { get; init; }
|
|
public int FailuresLastDay { get; init; }
|
|
|
|
public bool LastRunFailed => LastRunStatus == 0;
|
|
}
|
|
|
|
/// <param name="Type">CLUSTER_NODE, DISK_WITNESS, FILE_SHARE_WITNESS, CLOUD_WITNESS.</param>
|
|
/// <param name="State">0 offline, 1 up.</param>
|
|
public sealed record ClusterMember(string Name, string Type, int State, string StateText, int? QuorumVotes)
|
|
{
|
|
public bool IsUp => State == 1;
|
|
}
|