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;
}