Skip to content

Commit c82ae19

Browse files
Get-DbaAgRingBuffer - Add command for HADR ring buffer diagnostics (#10282)
1 parent d655259 commit c82ae19

4 files changed

Lines changed: 208 additions & 0 deletions

File tree

dbatools.psd1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@
196196
'Get-DbaAgHadr',
197197
'Get-DbaAgListener',
198198
'Get-DbaAgReplica',
199+
'Get-DbaAgRingBuffer',
199200
'Get-DbaAvailabilityGroup',
200201
'Get-DbaAvailableCollation',
201202
'Get-DbaBackupDevice',

dbatools.psm1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ if ($PSVersionTable.PSVersion.Major -lt 5) {
607607
'Mount-DbaDatabase',
608608
'Dismount-DbaDatabase',
609609
'Get-DbaAgReplica',
610+
'Get-DbaAgRingBuffer',
610611
'Get-DbaAgDatabase',
611612
'Get-DbaAgDatabaseReplicaState',
612613
'Get-DbaModule',

public/Get-DbaAgRingBuffer.ps1

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
function Get-DbaAgRingBuffer {
2+
<#
3+
.SYNOPSIS
4+
Retrieves Always On availability group diagnostic data from SQL Server's internal HADR ring buffers.
5+
6+
.DESCRIPTION
7+
This command queries sys.dm_os_ring_buffers for HADR-specific ring buffer types to provide diagnostic
8+
information about Always On availability groups. These ring buffers record state transitions, role changes,
9+
commit activity, and transport state events useful for troubleshooting AG health and failover issues.
10+
11+
As noted in Microsoft's documentation, the ring buffers are not officially supported, but they provide
12+
valuable post-mortem diagnostic data, especially when SQL Server stops responding or has crashed.
13+
14+
Reference: https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/always-on-ring-buffers
15+
16+
.PARAMETER SqlInstance
17+
The target SQL Server instance or instances.
18+
19+
.PARAMETER SqlCredential
20+
Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential).
21+
22+
Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported.
23+
24+
For MFA support, please use Connect-DbaInstance. To use:
25+
$cred = Get-Credential, this pass this $cred to the param.
26+
27+
Windows Authentication will be used if SqlCredential is not specified. To connect as a different Windows user, run PowerShell as that user.
28+
29+
.PARAMETER RingBufferType
30+
Specifies which HADR ring buffer types to query. Defaults to all four HADR ring buffer types.
31+
32+
Valid values:
33+
- RING_BUFFER_HADRDBMGR_API : State transitions at the API level
34+
- RING_BUFFER_HADRDBMGR_STATE : Database manager state change records
35+
- RING_BUFFER_HADRDBMGR_COMMIT : Commit-level activity records
36+
- RING_BUFFER_HADR_TRANSPORT_STATE: Connection and transport state transitions
37+
38+
.PARAMETER CollectionMinutes
39+
Specifies how many minutes of historical data to retrieve from the ring buffer. Defaults to 60 minutes.
40+
Use this to extend the analysis window when investigating longer-term AG issues or to focus on recent activity with shorter periods.
41+
42+
.PARAMETER EnableException
43+
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
44+
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
45+
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
46+
47+
.OUTPUTS
48+
PSCustomObject
49+
50+
Returns one object per ring buffer record retrieved from the SQL Server instance.
51+
52+
Properties:
53+
- ComputerName : The computer name of the SQL Server instance
54+
- InstanceName : The SQL Server instance name
55+
- SqlInstance : The full SQL Server instance name (computer\instance)
56+
- RingBufferType : The type of ring buffer (e.g. RING_BUFFER_HADRDBMGR_API)
57+
- RecordId : The unique record identifier from the ring buffer entry
58+
- EventTime : Approximate DateTime of the event (in local server time)
59+
- Record : The raw XML record containing event-specific diagnostic fields
60+
61+
.NOTES
62+
Tags: Diagnostic, Buffer, HADR, AvailabilityGroup, AG, AlwaysOn
63+
Author: the dbatools team + Claude
64+
65+
Website: https://dbatools.io
66+
Copyright: (c) 2018 by dbatools, licensed under MIT
67+
License: MIT https://opensource.org/licenses/MIT
68+
69+
.LINK
70+
https://dbatools.io/Get-DbaAgRingBuffer
71+
72+
.EXAMPLE
73+
PS C:\> Get-DbaAgRingBuffer -SqlInstance sql2019
74+
75+
Returns HADR ring buffer records from the last 60 minutes from the sql2019 instance.
76+
77+
.EXAMPLE
78+
PS C:\> Get-DbaAgRingBuffer -SqlInstance sql2019 -CollectionMinutes 240
79+
80+
Returns HADR ring buffer records from the last 240 minutes from the sql2019 instance.
81+
82+
.EXAMPLE
83+
PS C:\> Get-DbaAgRingBuffer -SqlInstance sql2019 -RingBufferType RING_BUFFER_HADRDBMGR_API
84+
85+
Returns only RING_BUFFER_HADRDBMGR_API records from the last 60 minutes from the sql2019 instance.
86+
87+
.EXAMPLE
88+
PS C:\> Get-DbaAgRingBuffer -SqlInstance sql2019 -RingBufferType RING_BUFFER_HADRDBMGR_API, RING_BUFFER_HADR_TRANSPORT_STATE
89+
90+
Returns API and transport state records from sql2019.
91+
92+
.EXAMPLE
93+
PS C:\> 'sql2019', 'sql2022' | Get-DbaAgRingBuffer
94+
95+
Returns all HADR ring buffer records from sql2019 and sql2022.
96+
#>
97+
[CmdletBinding()]
98+
Param (
99+
[parameter(Mandatory, ValueFromPipeline)]
100+
[DbaInstance[]]$SqlInstance,
101+
[PSCredential]$SqlCredential,
102+
[ValidateSet("RING_BUFFER_HADRDBMGR_API", "RING_BUFFER_HADRDBMGR_STATE", "RING_BUFFER_HADRDBMGR_COMMIT", "RING_BUFFER_HADR_TRANSPORT_STATE")]
103+
[string[]]$RingBufferType,
104+
[int]$CollectionMinutes = 60,
105+
[switch]$EnableException
106+
)
107+
108+
process {
109+
if (Test-FunctionInterrupt) { return }
110+
foreach ($instance in $SqlInstance) {
111+
try {
112+
$server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
113+
} catch {
114+
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
115+
}
116+
117+
$currentTimestamp = ($server.Query("SELECT cpu_ticks / CONVERT(FLOAT, (cpu_ticks / ms_ticks)) AS TimeStamp FROM sys.dm_os_sys_info"))[0]
118+
Write-Message -Level Verbose -Message "Using current timestamp of $currentTimestamp"
119+
120+
if ($RingBufferType) {
121+
$typeList = ($RingBufferType | ForEach-Object { "N'$_'" }) -join ", "
122+
} else {
123+
$typeList = "N'RING_BUFFER_HADRDBMGR_API', N'RING_BUFFER_HADRDBMGR_STATE', N'RING_BUFFER_HADRDBMGR_COMMIT', N'RING_BUFFER_HADR_TRANSPORT_STATE'"
124+
}
125+
126+
$sql = "WITH HadrRingBuffer AS
127+
(
128+
SELECT
129+
ring_buffer_type,
130+
timestamp,
131+
CONVERT(XML, record) AS record
132+
FROM sys.dm_os_ring_buffers
133+
WHERE ring_buffer_type IN ($typeList)
134+
)
135+
SELECT
136+
SERVERPROPERTY('ServerName') AS ServerName,
137+
ring_buffer_type,
138+
record.value('(./Record/@id)[1]', 'int') AS record_id,
139+
DATEADD(ms, -1 * ($currentTimestamp - [timestamp]), GETDATE()) AS EventTime,
140+
record
141+
FROM HadrRingBuffer
142+
WHERE DATEADD(ms, -1 * ($currentTimestamp - [timestamp]), GETDATE()) > DATEADD(MINUTE, -$CollectionMinutes, GETDATE())
143+
ORDER BY EventTime DESC;"
144+
145+
Write-Message -Level Verbose -Message "Executing SQL Statement: $sql"
146+
foreach ($row in $server.Query($sql)) {
147+
[PSCustomObject]@{
148+
ComputerName = $server.ComputerName
149+
InstanceName = $server.ServiceName
150+
SqlInstance = $server.DomainInstanceName
151+
RingBufferType = $row.ring_buffer_type
152+
RecordId = $row.record_id
153+
EventTime = $row.EventTime
154+
Record = $row.record
155+
}
156+
}
157+
}
158+
}
159+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0" }
2+
param(
3+
$ModuleName = "dbatools",
4+
$CommandName = "Get-DbaAgRingBuffer",
5+
$PSDefaultParameterValues = $TestConfig.Defaults
6+
)
7+
8+
Describe $CommandName -Tag UnitTests {
9+
Context "Parameter validation" {
10+
It "Should have the expected parameters" {
11+
$hasParameters = (Get-Command $CommandName).Parameters.Values.Name | Where-Object { $PSItem -notin ("WhatIf", "Confirm") }
12+
$expectedParameters = $TestConfig.CommonParameters
13+
$expectedParameters += @(
14+
"SqlInstance",
15+
"SqlCredential",
16+
"RingBufferType",
17+
"CollectionMinutes",
18+
"EnableException"
19+
)
20+
Compare-Object -ReferenceObject $expectedParameters -DifferenceObject $hasParameters | Should -BeNullOrEmpty
21+
}
22+
}
23+
}
24+
25+
Describe $CommandName -Tag IntegrationTests {
26+
Context "When retrieving HADR ring buffer data" {
27+
It "Returns results with expected properties" {
28+
$results = @(Get-DbaAgRingBuffer -SqlInstance $TestConfig.InstanceSingle)
29+
if ($results.Count -gt 0) {
30+
$results[0].PSObject.Properties.Name | Should -Contain "ComputerName"
31+
$results[0].PSObject.Properties.Name | Should -Contain "InstanceName"
32+
$results[0].PSObject.Properties.Name | Should -Contain "SqlInstance"
33+
$results[0].PSObject.Properties.Name | Should -Contain "RingBufferType"
34+
$results[0].PSObject.Properties.Name | Should -Contain "RecordId"
35+
$results[0].PSObject.Properties.Name | Should -Contain "EventTime"
36+
$results[0].PSObject.Properties.Name | Should -Contain "Record"
37+
}
38+
}
39+
40+
It "Filters by RingBufferType when specified" {
41+
$results = @(Get-DbaAgRingBuffer -SqlInstance $TestConfig.InstanceSingle -RingBufferType RING_BUFFER_HADRDBMGR_API)
42+
foreach ($result in $results) {
43+
$result.RingBufferType | Should -Be "RING_BUFFER_HADRDBMGR_API"
44+
}
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)