diff --git a/src/SQLInsight.Core/Data/Indexes.cs b/src/SQLInsight.Core/Data/Indexes.cs index 98850d6..9123ef6 100644 --- a/src/SQLInsight.Core/Data/Indexes.cs +++ b/src/SQLInsight.Core/Data/Indexes.cs @@ -70,7 +70,8 @@ public sealed record IndexInfo /// Since when SQLInsight has followed the usage (the restart before the first collection). public DateTimeOffset? TrackedSince { get; init; } - public string Key => $"{Schema}001f{Table}001f{IndexId}"; + /// Identifies the index within its database across collections (unit separator between the parts). + public string Key => $"{Schema}\u001f{Table}\u001f{IndexId}"; public long Reads => UserSeeks + UserScans + UserLookups; public double SizeMb => UsedPages * 8.0 / 1024; @@ -134,8 +135,10 @@ public static class IndexUsageTracking Indexes = d.Indexes.Select(i => { 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); - var writes = restarted || was is null ? i.UserUpdates : Math.Max(0, i.UserUpdates - was.UserUpdates); + // A counter lower than last time was reset without a restart (database taken offline or closed by + // 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) { deltas.Add(new IndexUsageDelta(d.Name, i.Schema, i.Table, i.Name, reads, writes)); diff --git a/tests/SQLInsight.Core.Tests/IndexUsageTests.cs b/tests/SQLInsight.Core.Tests/IndexUsageTests.cs index 5d2f3ac..ba5813c 100644 --- a/tests/SQLInsight.Core.Tests/IndexUsageTests.cs +++ b/tests/SQLInsight.Core.Tests/IndexUsageTests.cs @@ -30,4 +30,16 @@ public class IndexUsageTests 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)); + } }