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>
This commit is contained in:
parent
a01ec374a4
commit
4ceb64f2d5
2 changed files with 18 additions and 3 deletions
|
|
@ -70,7 +70,8 @@ public sealed record IndexInfo
|
||||||
/// <summary>Since when SQLInsight has followed the usage (the restart before the first collection).</summary>
|
/// <summary>Since when SQLInsight has followed the usage (the restart before the first collection).</summary>
|
||||||
public DateTimeOffset? TrackedSince { get; init; }
|
public DateTimeOffset? TrackedSince { get; init; }
|
||||||
|
|
||||||
public string Key => $"{Schema}001f{Table}001f{IndexId}";
|
/// <summary>Identifies the index within its database across collections (unit separator between the parts).</summary>
|
||||||
|
public string Key => $"{Schema}\u001f{Table}\u001f{IndexId}";
|
||||||
|
|
||||||
public long Reads => UserSeeks + UserScans + UserLookups;
|
public long Reads => UserSeeks + UserScans + UserLookups;
|
||||||
public double SizeMb => UsedPages * 8.0 / 1024;
|
public double SizeMb => UsedPages * 8.0 / 1024;
|
||||||
|
|
@ -134,8 +135,10 @@ public static class IndexUsageTracking
|
||||||
Indexes = d.Indexes.Select(i =>
|
Indexes = d.Indexes.Select(i =>
|
||||||
{
|
{
|
||||||
before.TryGetValue(d.Name + "\u001f" + i.Key, out var was);
|
before.TryGetValue(d.Name + "\u001f" + i.Key, out var was);
|
||||||
var reads = restarted || was is null ? i.Reads : Math.Max(0, i.Reads - was.Reads);
|
// A counter lower than last time was reset without a restart (database taken offline or closed by
|
||||||
var writes = restarted || was is null ? i.UserUpdates : Math.Max(0, i.UserUpdates - was.UserUpdates);
|
// AUTO_CLOSE, index rebuilt on 2012/2014): what it holds now is all new.
|
||||||
|
var reads = restarted || was is null || i.Reads < was.Reads ? i.Reads : i.Reads - was.Reads;
|
||||||
|
var writes = restarted || was is null || i.UserUpdates < was.UserUpdates ? i.UserUpdates : i.UserUpdates - was.UserUpdates;
|
||||||
if (reads > 0 || writes > 0)
|
if (reads > 0 || writes > 0)
|
||||||
{
|
{
|
||||||
deltas.Add(new IndexUsageDelta(d.Name, i.Schema, i.Table, i.Name, reads, writes));
|
deltas.Add(new IndexUsageDelta(d.Name, i.Schema, i.Table, i.Name, reads, writes));
|
||||||
|
|
|
||||||
|
|
@ -30,4 +30,16 @@ public class IndexUsageTests
|
||||||
Assert.Equal((5L, 200L), (delta.Reads, delta.Writes));
|
Assert.Equal((5L, 200L), (delta.Reads, delta.Writes));
|
||||||
Assert.Equal((2L, 50L), (afterRestart.UsageDeltas[0].Reads, afterRestart.UsageDeltas[0].Writes)); // counters started over
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue