Skip to content

Commit 53119b9

Browse files
Test-DbaAvailabilityGroup - Add comprehensive AG health monitoring
Implements comprehensive health monitoring for Availability Groups similar to SSMS AG Dashboard. Adds -HealthCheck switch parameter that returns detailed database replica state information including: - Replica-level health: Role, AvailabilityMode, FailoverMode, ConnectionState, SynchronizationState - Database-level status: SynchronizationState, IsFailoverReady, IsJoined, IsSuspended, SuspendReason - Performance metrics: LogSendQueue, RedoQueue sizes and rates - LSN tracking: LastCommit, LastHardened, LastSent, LastReceived, LastRedone with times and LSNs - Recovery metrics: EstimatedRecoveryTime, EstimatedDataLoss, SynchronizationPerformance Completes feature request from issue #2190 based on research and code proposals from @andreasjordan and @ReeceGoding, referencing Microsoft AG monitoring documentation. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Chrissy LeMaire <potatoqualitee@users.noreply.github.com>
1 parent 2e3fb63 commit 53119b9

File tree

2 files changed

+74
-11
lines changed

2 files changed

+74
-11
lines changed

public/Test-DbaAvailabilityGroup.ps1

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ function Test-DbaAvailabilityGroup {
4848
Validates that the most recent database backup chain can be used for AG database addition.
4949
Enables validation using existing backups instead of creating new ones, but requires the last backup to be a transaction log backup. Use this to test AG readiness with your current backup strategy.
5050
51+
.PARAMETER HealthCheck
52+
Performs comprehensive health monitoring of the Availability Group similar to SSMS AG Dashboard.
53+
Returns detailed replica and database synchronization status including queue sizes, LSN information, and performance metrics. Use this to monitor AG health, identify synchronization issues, or troubleshoot failover readiness.
54+
5155
.PARAMETER EnableException
5256
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
5357
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
@@ -73,21 +77,27 @@ function Test-DbaAvailabilityGroup {
7377
PS C:\> Test-DbaAvailabilityGroup -SqlInstance SQL2016 -AvailabilityGroup TestAG1 -AddDatabase AdventureWorks -SeedingMode Automatic
7478
7579
Test if database AdventureWorks can be added to the Availability Group TestAG1 with automatic seeding.
80+
81+
.EXAMPLE
82+
PS C:\> Test-DbaAvailabilityGroup -SqlInstance SQL2016 -AvailabilityGroup TestAG1 -HealthCheck
83+
84+
Performs comprehensive health monitoring of TestAG1, returning detailed synchronization status for all replicas and databases similar to SSMS AG Dashboard.
7685
#>
7786
[CmdletBinding()]
7887
param (
79-
[Parameter(Mandatory = $true, Position = 0)]
88+
[Parameter(Mandatory)]
8089
[DbaInstanceParameter]$SqlInstance,
8190
[PSCredential]$SqlCredential,
82-
[Parameter(Mandatory = $true)]
91+
[Parameter(Mandatory)]
8392
[string]$AvailabilityGroup,
8493
[DbaInstanceParameter[]]$Secondary,
8594
[PSCredential]$SecondarySqlCredential,
8695
[string[]]$AddDatabase,
87-
[ValidateSet('Automatic', 'Manual')]
96+
[ValidateSet("Automatic", "Manual")]
8897
[string]$SeedingMode,
8998
[string]$SharedPath,
9099
[switch]$UseLastBackup,
100+
[switch]$HealthCheck,
91101
[switch]$EnableException
92102
)
93103
process {
@@ -110,22 +120,75 @@ function Test-DbaAvailabilityGroup {
110120
return
111121
}
112122

113-
if ($ag.LocalReplicaRole -ne 'Primary') {
123+
if (-not $HealthCheck -and $ag.LocalReplicaRole -ne 'Primary') {
114124
Stop-Function -Message "LocalReplicaRole of replica $server is not Primary, but $($ag.LocalReplicaRole). Please connect to the current primary replica $($ag.PrimaryReplica)."
115125
return
116126
}
117127

118128
# Test for health of Availability Group
119129

120-
# Later: Get replica and database states like in SSMS dashboard
121-
# Now: Just test for ConnectionState -eq 'Connected'
130+
if ($HealthCheck) {
131+
# Comprehensive health monitoring similar to SSMS AG Dashboard
132+
# Returns detailed database replica state information for all replicas
133+
134+
foreach ($replica in $ag.AvailabilityReplicas) {
135+
$replicaId = $replica.UniqueId
136+
$replicaStates = $ag.DatabaseReplicaStates | Where-Object AvailabilityReplicaId -eq $replicaId
122137

123-
# Note on further development:
124-
# As long as there are no databases in the Availability Group, test for RollupSynchronizationState is not useful
138+
foreach ($db in $ag.AvailabilityDatabases) {
139+
$databaseReplicaState = $replicaStates | Where-Object AvailabilityDateabaseId -eq $db.UniqueId
140+
if ($null -eq $databaseReplicaState) {
141+
continue
142+
}
125143

126-
# The primary replica always has the best information about all the replicas.
127-
# We can maybe also connect to the secondary replicas and test their view of the situation, but then only test the local replica.
144+
$splatOutput = @{
145+
ComputerName = $ag.ComputerName
146+
InstanceName = $ag.InstanceName
147+
SqlInstance = $ag.SqlInstance
148+
AvailabilityGroup = $ag.Name
149+
PrimaryReplica = $ag.PrimaryReplica
150+
ReplicaServerName = $databaseReplicaState.AvailabilityReplicaServerName
151+
ReplicaRole = $databaseReplicaState.ReplicaRole
152+
ReplicaAvailabilityMode = $replica.AvailabilityMode
153+
ReplicaFailoverMode = $replica.FailoverMode
154+
ReplicaConnectionState = $replica.ConnectionState
155+
ReplicaJoinState = $replica.JoinState
156+
ReplicaSynchronizationState = $replica.RollupSynchronizationState
157+
DatabaseName = $databaseReplicaState.AvailabilityDatabaseName
158+
SynchronizationState = $databaseReplicaState.SynchronizationState
159+
IsFailoverReady = $databaseReplicaState.IsFailoverReady
160+
IsJoined = $databaseReplicaState.IsJoined
161+
IsSuspended = $databaseReplicaState.IsSuspended
162+
SuspendReason = $databaseReplicaState.SuspendReason
163+
EstimatedRecoveryTime = $databaseReplicaState.EstimatedRecoveryTime
164+
EstimatedDataLoss = $databaseReplicaState.EstimatedDataLoss
165+
SynchronizationPerformance = $databaseReplicaState.SynchronizationPerformance
166+
LogSendQueueSize = $databaseReplicaState.LogSendQueueSize
167+
LogSendRate = $databaseReplicaState.LogSendRate
168+
RedoQueueSize = $databaseReplicaState.RedoQueueSize
169+
RedoRate = $databaseReplicaState.RedoRate
170+
FileStreamSendRate = $databaseReplicaState.FileStreamSendRate
171+
EndOfLogLSN = $databaseReplicaState.EndOfLogLSN
172+
RecoveryLSN = $databaseReplicaState.RecoveryLSN
173+
TruncationLSN = $databaseReplicaState.TruncationLSN
174+
LastCommitLSN = $databaseReplicaState.LastCommitLSN
175+
LastCommitTime = $databaseReplicaState.LastCommitTime
176+
LastHardenedLSN = $databaseReplicaState.LastHardenedLSN
177+
LastHardenedTime = $databaseReplicaState.LastHardenedTime
178+
LastReceivedLSN = $databaseReplicaState.LastReceivedLSN
179+
LastReceivedTime = $databaseReplicaState.LastReceivedTime
180+
LastRedoneLSN = $databaseReplicaState.LastRedoneLSN
181+
LastRedoneTime = $databaseReplicaState.LastRedoneTime
182+
LastSentLSN = $databaseReplicaState.LastSentLSN
183+
LastSentTime = $databaseReplicaState.LastSentTime
184+
}
185+
[PSCustomObject]$splatOutput
186+
}
187+
}
188+
return
189+
}
128190

191+
# Basic connectivity test for non-HealthCheck scenarios
129192
$failure = $false
130193
foreach ($replica in $ag.AvailabilityReplicas) {
131194
if ($replica.ConnectionState -ne 'Connected') {
@@ -138,7 +201,6 @@ function Test-DbaAvailabilityGroup {
138201
return
139202
}
140203

141-
142204
# For now, just output the base information.
143205

144206
if (-not $AddDatabase) {

tests/Test-DbaAvailabilityGroup.Tests.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Describe $CommandName -Tag UnitTests {
2020
"SeedingMode",
2121
"SharedPath",
2222
"UseLastBackup",
23+
"HealthCheck",
2324
"EnableException"
2425
)
2526
Compare-Object -ReferenceObject $expectedParameters -DifferenceObject $hasParameters | Should -BeNullOrEmpty

0 commit comments

Comments
 (0)