A five-minute collector reads tempdb space (free, version store, user and
internal objects, whether files can grow), the 'Version Store Size (KB)'
counter, the sessions using most tempdb, the oldest snapshot transaction
and, only when the version store is large, the databases generating
versions. Trend and heavy sessions survive restarts; used space and the
version store become metrics with explanation and assessment.
Rules SQLI-TEMP-001 (version store > 1 GB or steadily growing),
SQLI-TEMP-002 (nearly full and cannot grow) and SQLI-TEMP-003 (session
above 1 GB for 15 minutes), configurable under SQLInsight:Tempdb.
"tempdb lige nu" on the instance page; GET /api/v1/instances/{name}/tempdb.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
82 lines
4.2 KiB
C#
82 lines
4.2 KiB
C#
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Mvc.Testing;
|
|
using Microsoft.Data.SqlClient;
|
|
using SQLInsight.Collectors.Instance;
|
|
using SQLInsight.Core.Data;
|
|
using SQLInsight.Core.Instances;
|
|
using SQLInsight.Repository;
|
|
|
|
namespace SQLInsight.IntegrationTests;
|
|
|
|
/// <summary>tempdb and the version store as the least-privilege agent reads them.</summary>
|
|
[Collection(SqlServerTestGroup.Name)]
|
|
[Trait("Category", "Integration")]
|
|
public class TempdbTests(SqlServerFixture sql)
|
|
{
|
|
[Fact]
|
|
public async Task Space_version_store_and_the_session_filling_tempdb_are_read()
|
|
{
|
|
await using var user = new SqlConnection(new SqlConnectionStringBuilder(sql.SaConnectionString) { ApplicationName = "Rapport" }.ConnectionString);
|
|
await user.OpenAsync();
|
|
await using (var fill = new SqlCommand("CREATE TABLE #big (Id int, Pad char(4000)); INSERT #big SELECT TOP (3000) 1, 'x' FROM sys.all_objects a CROSS JOIN sys.all_objects b;", user))
|
|
{
|
|
await fill.ExecuteNonQueryAsync();
|
|
}
|
|
|
|
await using var session = await AgentSession.OpenAsync(sql);
|
|
var first = await session.CollectAsync<TempdbSnapshot>(new TempdbCollector(sessionMegabytes: 1));
|
|
var second = await session.CollectAsync(new TempdbCollector(sessionMegabytes: 1), first);
|
|
|
|
Assert.True(first.SizeMegabytes > 0);
|
|
Assert.InRange(first.FreeMegabytes, 0, first.SizeMegabytes);
|
|
Assert.True(first.VersionStoreMegabytes >= 0);
|
|
var heavy = Assert.Single(first.TopSessions, s => s.ProgramName == "Rapport");
|
|
Assert.True(heavy.UsedMegabytes >= 10);
|
|
Assert.Equal(2, second.VersionStoreHistory.Count);
|
|
Assert.Equal(first.HeavySessionsSince[heavy.Key], second.HeavySessionsSince[heavy.Key]); // the start is kept
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Instance_page_and_api_show_tempdb_with_logins_left_out()
|
|
{
|
|
var database = "SQLInsightTmp_" + Guid.NewGuid().ToString("N")[..8];
|
|
await sql.ExecuteAsSaAsync($"CREATE DATABASE [{database}];");
|
|
try
|
|
{
|
|
var repository = new InsightRepository(sql.DatabaseConnectionString(database));
|
|
await repository.MigrateAsync();
|
|
var now = DateTimeOffset.UtcNow;
|
|
var id = await repository.UpsertInstanceAsync("prod-sql01", new InstanceInfo
|
|
{
|
|
ServerName = "SQL01", Version = SqlServerVersion.Parse("16.0.4135.4"), ProductLevel = "RTM", Edition = "Standard Edition", EngineEdition = EngineEdition.Standard,
|
|
}, "SqlClient", false, now);
|
|
await repository.SaveCollectedAsync(id, "tempdb", new TempdbSnapshot
|
|
{
|
|
CapturedAt = now, SizeMegabytes = 8192, FreeMegabytes = 1024, VersionStoreMegabytes = 2048,
|
|
TopSessions = [new TempdbSession(70, now.AddHours(-1), 3000, @"KUNDE\anne", "SSMS", "PC01", "running")],
|
|
VersionGenerators = [new VersionGenerator("AXDB", 1800)],
|
|
});
|
|
|
|
await using var factory = new WebApplicationFactory<Program>().WithWebHostBuilder(b =>
|
|
{
|
|
b.UseEnvironment("Development");
|
|
b.UseSetting("SQLInsight:Repository:ConnectionString", sql.DatabaseConnectionString(database));
|
|
b.UseSetting("SQLInsight:Web:Authentication", "None");
|
|
});
|
|
var client = factory.CreateClient();
|
|
var page = await client.GetStringAsync("/instanser/prod-sql01");
|
|
var api = await client.GetStringAsync("/api/v1/instances/prod-sql01/tempdb");
|
|
|
|
Assert.Contains("tempdb lige nu", page, StringComparison.Ordinal);
|
|
Assert.Contains("AXDB", page, StringComparison.Ordinal);
|
|
Assert.Contains("Version store (MB)", page, StringComparison.Ordinal); // the graph with its explanation
|
|
Assert.Contains("\"versionStoreMegabytes\":2048", api, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("anne", api, StringComparison.Ordinal);
|
|
Assert.Single(await repository.GetMetricSamplesAsync(id, Metrics.VersionStoreMegabytes, now.AddMinutes(-5), now.AddMinutes(5)));
|
|
}
|
|
finally
|
|
{
|
|
await sql.ExecuteAsSaAsync($"ALTER DATABASE [{database}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE [{database}];");
|
|
}
|
|
}
|
|
}
|