using SQLInsight.Core.Data; using SQLInsight.Core.Instances; namespace SQLInsight.Collectors.Performance; /// /// Active session sampling: every 15 seconds, what each waiting user request waits on, in which database and for /// which statement (the same key as top SQL). Sums of these samples show which statements a wait type comes from. /// The agent's own sessions and system sessions are left out. One light query on sys.dm_exec_requests; SQL Server 2005+. /// public sealed class WaitSamplingCollector : Collector { private const string From = """ FROM sys.dm_exec_requests r JOIN sys.dm_exec_sessions s ON s.session_id = r.session_id OUTER APPLY sys.dm_exec_sql_text(r.sql_handle) st WHERE s.is_user_process = 1 AND r.session_id <> @@SPID AND r.wait_type IS NOT NULL AND (s.program_name IS NULL OR s.program_name NOT LIKE N'SQLInsight Pro%') """; private const string Columns = """ r.session_id, r.wait_type, r.wait_time, DB_NAME(r.database_id), r.blocking_session_id, """; private const string Text = """ CAST(SUBSTRING(st.text, r.statement_start_offset / 2 + 1, CASE WHEN r.statement_end_offset = -1 THEN 4000 ELSE (r.statement_end_offset - r.statement_start_offset) / 2 + 1 END) AS nvarchar(4000)) """; /// The statement key as top SQL makes it on 2008+: query_hash. internal const string Sql2008 = "SELECT " + Columns + """ CASE WHEN r.query_hash IS NULL OR r.query_hash = 0x0000000000000000 THEN NULL ELSE CONVERT(varchar(20), r.query_hash, 1) END, """ + "\n" + Text + "\n" + From; /// SQL Server 2005 has no query_hash: the batch (sql_handle) and the statement's offsets, as top SQL. internal const string Sql2005 = "SELECT " + Columns + """ CASE WHEN r.sql_handle IS NULL THEN NULL ELSE sys.fn_varbintohexstr(r.sql_handle) + ':' + CAST(r.statement_start_offset AS varchar(12)) + ':' + CAST(r.statement_end_offset AS varchar(12)) END, """ + "\n" + Text + "\n" + From; public override string Name => "wait-sampling"; public override TimeSpan DefaultInterval => WaitSampling.Interval; public override IReadOnlyCollection RequiredCapabilities => [Capability.DynamicManagementViews]; public override bool IsSampling => true; protected override async Task CollectAsync(CollectorContext context, WaitSampleSnapshot? previous) { var sql = context.Instance.Version.Major >= 10 ? Sql2008 : Sql2005; var requests = await Db.QueryAsync(context.Connection, sql, r => new WaitingRequest( r.Int(0), r.String(1), r.Long(2), r.NullableString(3), r.NullableString(5), r.NullableString(6), r.NullableInt(4) ?? 0), context.CancellationToken).ConfigureAwait(false); return WaitSampling.Aggregate(requests, context.Now); } }