A new light collector samples sys.dm_exec_requests every 15 seconds:
waiting user sessions (not the agent's own) by wait type, database and
statement (same key as top SQL), with blocking. Sampling-only cycles
reuse the last probe and skip rule evaluation. Stored in
insight.ActiveWaitSamples (migration 0010) for RawDays.
Wait types on the instance page and in the analysis link to
/instanser/{navn}/ventetyper/{type}: Danish explanation from a new
knowledge base (shared with the SQLI-WAIT rules, with Dynamics AX notes),
trend over time, the statements that waited with share and estimated
wait, a link to the full text (/instanser/{navn}/sql) and, for lock
waits, to blocking in the same window. SQL Server 2005 compatible.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
68 lines
2.8 KiB
C#
68 lines
2.8 KiB
C#
using SQLInsight.Core.Data;
|
|
|
|
namespace SQLInsight.Core.Tests;
|
|
|
|
public class WaitSamplingTests
|
|
{
|
|
private static readonly DateTimeOffset Now = new(2026, 10, 2, 8, 0, 0, TimeSpan.Zero);
|
|
|
|
[Theory]
|
|
[InlineData("LCK_M_X", "LCK_M_*")]
|
|
[InlineData("lck_m_sch_m", "LCK_M_*")]
|
|
[InlineData("PAGEIOLATCH_SH", "PAGEIOLATCH_*")]
|
|
[InlineData("CXPACKET", "CXPACKET, CXCONSUMER, CXSYNC_*")]
|
|
[InlineData("CXSYNC_PORT", "CXPACKET, CXCONSUMER, CXSYNC_*")]
|
|
[InlineData("RESOURCE_SEMAPHORE_QUERY_COMPILE", "RESOURCE_SEMAPHORE_QUERY_COMPILE")]
|
|
[InlineData("RESOURCE_SEMAPHORE", "RESOURCE_SEMAPHORE")]
|
|
[InlineData("PREEMPTIVE_OS_AUTHENTICATIONOPS", "PREEMPTIVE_*")]
|
|
[InlineData("BACKUPBUFFER", "BACKUPIO, BACKUPBUFFER")]
|
|
[InlineData("LATCH_EX", "LATCH_*")]
|
|
public void Wait_types_find_their_family(string waitType, string family)
|
|
{
|
|
var explanation = WaitKnowledge.Lookup(waitType);
|
|
|
|
Assert.True(explanation.Known);
|
|
Assert.Equal(family, explanation.Family);
|
|
Assert.False(string.IsNullOrWhiteSpace(explanation.Actions));
|
|
}
|
|
|
|
[Fact]
|
|
public void Parallelism_mentions_maxdop_1_for_dynamics_ax_and_shares_the_rule_text()
|
|
{
|
|
var cx = WaitKnowledge.Lookup("CXPACKET");
|
|
|
|
Assert.Contains("MAXDOP 1", cx.DynamicsAx, StringComparison.Ordinal);
|
|
Assert.Same(WaitKnowledge.Parallelism, cx);
|
|
}
|
|
|
|
[Fact]
|
|
public void Unknown_wait_types_get_a_generic_text_with_their_category()
|
|
{
|
|
var unknown = WaitKnowledge.Lookup("SOME_NEW_WAIT_2027");
|
|
|
|
Assert.False(unknown.Known);
|
|
Assert.Contains("SOME_NEW_WAIT_2027", unknown.Meaning, StringComparison.Ordinal);
|
|
Assert.Equal("Baggrundsventetid uden betydning", WaitKnowledge.Lookup("LAZYWRITER_SLEEP").Title);
|
|
}
|
|
|
|
[Fact]
|
|
public void Samples_are_summed_by_wait_type_database_and_statement_without_background_waits()
|
|
{
|
|
var requests = new[]
|
|
{
|
|
new WaitingRequest(51, "LCK_M_S", 2000, "AX", "0xAA", "SELECT * FROM INVENTTRANS", 60),
|
|
new WaitingRequest(52, "lck_m_s", 500, "AX", "0xAA", "SELECT * FROM INVENTTRANS", 60),
|
|
new WaitingRequest(53, "PAGEIOLATCH_SH", 12, "AX", "0xBB", null, 0),
|
|
new WaitingRequest(54, "SLEEP_TASK", 1000, null, null, null, 0),
|
|
};
|
|
|
|
var snapshot = WaitSampling.Aggregate(requests, Now);
|
|
|
|
Assert.Equal(2, snapshot.Rows.Count);
|
|
var locks = snapshot.Rows[0];
|
|
Assert.Equal(("LCK_M_S", 2, 2500L, 2000L, 2), (locks.WaitType, locks.Sessions, locks.WaitTimeMs, locks.MaxWaitTimeMs, locks.BlockedSessions));
|
|
Assert.Equal(2, snapshot.Statements.Count);
|
|
Assert.Equal("SELECT * FROM INVENTTRANS", snapshot.Statements.Single(s => s.QueryKey == "0xAA").Text);
|
|
Assert.True(WaitSampling.IsLockWait("LCK_M_IX"));
|
|
}
|
|
}
|