namespace SQLInsight.Core.Data; /// /// Selected SQL Server performance counters (sys.dm_os_performance_counters / sysperfinfo). /// Point-in-time counters are used as-is; per-second counters are cumulative and need a delta. /// public sealed record PerformanceCounterSnapshot { public required DateTimeOffset CapturedAt { get; init; } public required DateTime SqlServerStartTime { get; init; } public long? MemoryGrantsPending { get; init; } public long? MemoryGrantsOutstanding { get; init; } public long? PageLifeExpectancySeconds { get; init; } public long? UserConnections { get; init; } public long? ProcessesBlocked { get; init; } /// Cumulative since start. public long? BatchRequests { get; init; } /// Cumulative since start. public long? SqlCompilations { get; init; } /// Cumulative since start. public long? SqlRecompilations { get; init; } } public sealed record PerformanceCounterRates { public required DateTimeOffset From { get; init; } public required DateTimeOffset To { get; init; } public double? BatchRequestsPerSecond { get; init; } public double? CompilationsPerSecond { get; init; } public double? RecompilationsPerSecond { get; init; } public static PerformanceCounterRates? Compute(PerformanceCounterSnapshot? previous, PerformanceCounterSnapshot current) { ArgumentNullException.ThrowIfNull(current); if (previous is null || previous.SqlServerStartTime != current.SqlServerStartTime || current.CapturedAt <= previous.CapturedAt) { return null; } var seconds = (current.CapturedAt - previous.CapturedAt).TotalSeconds; return new PerformanceCounterRates { From = previous.CapturedAt, To = current.CapturedAt, BatchRequestsPerSecond = Rate(previous.BatchRequests, current.BatchRequests, seconds), CompilationsPerSecond = Rate(previous.SqlCompilations, current.SqlCompilations, seconds), RecompilationsPerSecond = Rate(previous.SqlRecompilations, current.SqlRecompilations, seconds), }; } private static double? Rate(long? before, long? after, double seconds) => before is { } b && after is { } a && a >= b ? (a - b) / seconds : null; }