sqlinsight-pro/tests/SQLInsight.Core.Tests/IndexUsageTests.cs
Søren Eggert Lundsteen Olsen 4ceb64f2d5 Review 1.2: index usage after a counter reset without a restart
sys.dm_db_index_usage_stats also starts over when a database is taken
offline, closed by AUTO_CLOSE or (2012/2014) an index is rebuilt. The
delta was then clamped to 0, so the usage since the reset was lost from
the daily history and the tracked totals. A lower counter now counts as
new usage. IndexInfo.Key also used the literal text "001f" as its
separator instead of the unit separator character.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 11:34:17 +02:00

45 lines
2.4 KiB
C#

using SQLInsight.Core.Data;
namespace SQLInsight.Core.Tests;
public class IndexUsageTests
{
private static readonly DateTime Start = new(2026, 9, 1, 6, 0, 0, DateTimeKind.Utc);
private static IndexSnapshot Snapshot(DateTime start, DateTimeOffset at, long reads, long writes) => new()
{
CapturedAt = at,
SqlServerStartTime = start,
Databases = [new IndexDatabase { Name = "AX", Indexes = [new IndexInfo { Schema = "dbo", Table = "INVENTTRANS", IndexId = 5, Name = "I_177ITEMIDX", Type = "NONCLUSTERED", UserSeeks = reads, UserUpdates = writes }] }],
};
[Fact]
public void Usage_is_summed_across_restarts_and_tracked_from_the_first_restart_seen()
{
var first = IndexUsageTracking.Advance(null, Snapshot(Start, new DateTimeOffset(Start.AddDays(1)), 10, 100));
var second = IndexUsageTracking.Advance(first, Snapshot(Start, new DateTimeOffset(Start.AddDays(2)), 15, 300));
var restart = Start.AddDays(3);
var afterRestart = IndexUsageTracking.Advance(second, Snapshot(restart, new DateTimeOffset(restart.AddHours(6)), 2, 50));
var index = afterRestart.Databases[0].Indexes[0];
Assert.Equal((17L, 350L), (index.TrackedReads, index.TrackedWrites));
Assert.Equal(new DateTimeOffset(Start), index.TrackedSince);
Assert.Equal(TimeSpan.FromDays(3.25), afterRestart.TrackedFor(index));
Assert.Equal(TimeSpan.FromHours(6), afterRestart.Uptime);
var delta = Assert.Single(second.UsageDeltas);
Assert.Equal((5L, 200L), (delta.Reads, delta.Writes));
Assert.Equal((2L, 50L), (afterRestart.UsageDeltas[0].Reads, afterRestart.UsageDeltas[0].Writes)); // counters started over
}
[Fact]
public void A_counter_reset_without_a_restart_counts_what_the_counter_holds_now()
{
// The database was taken offline (or closed by AUTO_CLOSE) between the collections: same start time, lower counters.
var first = IndexUsageTracking.Advance(null, Snapshot(Start, new DateTimeOffset(Start.AddDays(1)), 100, 1000));
var second = IndexUsageTracking.Advance(first, Snapshot(Start, new DateTimeOffset(Start.AddDays(2)), 7, 40));
var delta = Assert.Single(second.UsageDeltas);
Assert.Equal((7L, 40L), (delta.Reads, delta.Writes));
Assert.Equal((107L, 1040L), (second.Databases[0].Indexes[0].TrackedReads, second.Databases[0].Indexes[0].TrackedWrites));
}
}