sqlinsight-pro/src/SQLInsight.Collectors/ICollector.cs
Claude 63f23c4bbd
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
Fase 1: collectors and rule pack (41 rules)
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
2026-09-29 06:17:08 +00:00

49 lines
1.7 KiB
C#

using System.Data.Common;
using SQLInsight.Core.Instances;
namespace SQLInsight.Collectors;
/// <summary>
/// Reads one kind of data from a monitored instance. Collectors are stateless; anything they need from the
/// previous run (to compute deltas or continue reading) is passed in <see cref="CollectorContext.Previous"/>.
/// </summary>
public interface ICollector
{
string Name { get; }
/// <summary>The type of the collected data, as rules look it up in <c>CollectedData</c>.</summary>
Type DataType { get; }
TimeSpan DefaultInterval { get; }
/// <summary>What the instance must support. If anything is missing, the collector does not run.</summary>
IReadOnlyCollection<Capability> RequiredCapabilities { get; }
Task<object> CollectAsync(CollectorContext context);
}
/// <param name="Previous">This collector's result from the previous run on the same instance, if any.</param>
public sealed record CollectorContext(
DbConnection Connection,
InstanceInfo Instance,
InstanceCapabilities Capabilities,
DateTimeOffset Now,
object? Previous,
CancellationToken CancellationToken);
/// <summary>
/// Typed base class for collectors.
/// </summary>
public abstract class Collector<T> : ICollector
where T : class
{
public abstract string Name { get; }
public Type DataType => typeof(T);
public abstract TimeSpan DefaultInterval { get; }
public virtual IReadOnlyCollection<Capability> RequiredCapabilities => [];
async Task<object> ICollector.CollectAsync(CollectorContext context) =>
await CollectAsync(context, context.Previous as T).ConfigureAwait(false);
protected abstract Task<T> CollectAsync(CollectorContext context, T? previous);
}