Some checks are pending
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
CI / Build og unit tests (push) Waiting to run
Collectors (SQL Server 2000+, version-specific SQL, least privilege): server, configuration, trace flags, databases (settings, backups in UTC, CHECKDB, VLFs, Query Store, files, tempdb), suspect pages, plan cache, wait stats and file I/O as deltas with restart/clear detection, performance counters, CPU from the scheduler monitor ring buffer, blocking (sleeping head blockers with open transactions), and deadlocks from system_health continuing from the last file offset. Rules: configuration (memory, MAXDOP per Microsoft NUMA guidance, cost threshold, ad hoc bloat, risky options, pending changes), databases (state, settings, backups, log backups, CHECKDB, suspect pages, VLFs, autogrowth, max size, Query Store, tempdb), waits (THREADPOOL, signal waits, locks, I/O, WRITELOG, parallelism, PAGELATCH), I/O latency, CPU, memory grants, blocking and deadlocks. Missing data makes a rule not measurable with the collector's reason. Integration tests run every collector as the least-privilege login and reproduce real blocking and a real deadlock; green on 2017 and 2022. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
69 lines
2.8 KiB
C#
69 lines
2.8 KiB
C#
using System.Xml.Linq;
|
||
using SQLInsight.Collectors.Concurrency;
|
||
|
||
namespace SQLInsight.Collectors.Tests;
|
||
|
||
public class DeadlockParserTests
|
||
{
|
||
private const string Graph = """
|
||
<deadlock>
|
||
<victim-list><victimProcess id="process1" /></victim-list>
|
||
<process-list>
|
||
<process id="process1" spid="60" waitresource="KEY: 5:72057594043236352 (8194443284a0)" lockMode="U" isolationlevel="read committed (2)"
|
||
clientapp="Sales.Api" hostname="WEB01" loginname="app" currentdb="5" currentdbname="Sales">
|
||
<inputbuf> UPDATE dbo.Orders SET Status = 2 WHERE Id = @id </inputbuf>
|
||
</process>
|
||
<process id="process2" spid="61" waitresource="KEY: 5:72057594043236352 (61a06abd401c)" lockMode="U" currentdb="5">
|
||
<inputbuf>UPDATE dbo.Orders SET Status = 3 WHERE Id = @id</inputbuf>
|
||
</process>
|
||
</process-list>
|
||
<resource-list />
|
||
</deadlock>
|
||
""";
|
||
|
||
[Fact]
|
||
public void Parses_event_file_format()
|
||
{
|
||
var evt = XElement.Parse($"""
|
||
<event name="xml_deadlock_report" timestamp="2026-09-29T10:15:30.123Z">
|
||
<data name="xml_report"><value>{Graph}</value></data>
|
||
</event>
|
||
""");
|
||
|
||
var deadlock = DeadlockParser.ParseEvent(evt)!;
|
||
|
||
Assert.Equal(new DateTimeOffset(2026, 9, 29, 10, 15, 30, 123, TimeSpan.Zero), deadlock.Time);
|
||
Assert.Equal(2, deadlock.Processes.Count);
|
||
var victim = Assert.Single(deadlock.Processes, p => p.IsVictim);
|
||
Assert.Equal(60, victim.SessionId);
|
||
Assert.Equal("Sales", victim.DatabaseName);
|
||
Assert.Equal("Sales.Api", victim.ClientApp);
|
||
Assert.Equal("UPDATE dbo.Orders SET Status = 2 WHERE Id = @id", victim.InputBuffer);
|
||
Assert.Equal("5", deadlock.Processes[1].DatabaseName); // no currentdbname on older builds
|
||
Assert.StartsWith("<deadlock>", deadlock.GraphXml, StringComparison.Ordinal);
|
||
}
|
||
|
||
[Fact]
|
||
public void Parses_sql2008_escaped_graph()
|
||
{
|
||
// SQL Server 2008 RTM–SP1 put the graph in the ring buffer as escaped text.
|
||
var escaped = System.Security.SecurityElement.Escape(Graph);
|
||
var evt = XElement.Parse($"""
|
||
<event name="xml_deadlock_report" timestamp="2026-09-29T10:15:30Z">
|
||
<data name="xml_report"><type name="unicode_string" /><value>{escaped}</value></data>
|
||
</event>
|
||
""");
|
||
|
||
var deadlock = DeadlockParser.ParseEvent(evt)!;
|
||
|
||
Assert.Equal(2, deadlock.Processes.Count);
|
||
}
|
||
|
||
[Fact]
|
||
public void Garbage_is_ignored()
|
||
{
|
||
var evt = XElement.Parse("""<event name="xml_deadlock_report" timestamp="2026-09-29T10:15:30Z"><data name="xml_report"><value>not xml</value></data></event>""");
|
||
|
||
Assert.Null(DeadlockParser.ParseEvent(evt));
|
||
}
|
||
}
|