Review 1.2: scan the version store for its generators at most hourly
sys.dm_tran_top_version_generators reads the whole version store. It ran every 5 minutes while the version store was over half the threshold, i.e. exactly when it is large and expensive to scan, and a timeout failed the whole tempdb collection. It now runs at most once per hour (the previous answer is kept in between, with its time) and a failed scan keeps the previous answer. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4ceb64f2d5
commit
d070cd872b
2 changed files with 31 additions and 3 deletions
|
|
@ -42,6 +42,9 @@ public sealed class TempdbCollector(int versionStoreMegabytes = 1024, int sessio
|
||||||
ORDER BY t.elapsed_time_seconds DESC
|
ORDER BY t.elapsed_time_seconds DESC
|
||||||
""";
|
""";
|
||||||
|
|
||||||
|
/// <summary>How often the version store is scanned for the databases generating it, while it is large.</summary>
|
||||||
|
public static readonly TimeSpan GeneratorsInterval = TimeSpan.FromHours(1);
|
||||||
|
|
||||||
// Scans the version store: only run when it is large enough to need explaining.
|
// Scans the version store: only run when it is large enough to need explaining.
|
||||||
internal const string GeneratorsSql = """
|
internal const string GeneratorsSql = """
|
||||||
SELECT TOP 10 DB_NAME(database_id), SUM(CAST(aggregated_record_length_in_bytes AS bigint))
|
SELECT TOP 10 DB_NAME(database_id), SUM(CAST(aggregated_record_length_in_bytes AS bigint))
|
||||||
|
|
@ -68,9 +71,30 @@ public sealed class TempdbCollector(int versionStoreMegabytes = 1024, int sessio
|
||||||
var oldest = (await Db.QueryAsync(connection, OldestSnapshotSql, r => new SnapshotTransaction(r.Int(0), r.NullableLong(1) ?? 0, r.NullableString(2), r.NullableString(3)), ct)
|
var oldest = (await Db.QueryAsync(connection, OldestSnapshotSql, r => new SnapshotTransaction(r.Int(0), r.NullableLong(1) ?? 0, r.NullableString(2), r.NullableString(3)), ct)
|
||||||
.ConfigureAwait(false)).FirstOrDefault();
|
.ConfigureAwait(false)).FirstOrDefault();
|
||||||
var versionMb = space.VersionKb is { } kb ? kb / 1024.0 : space.Version * PageMb;
|
var versionMb = space.VersionKb is { } kb ? kb / 1024.0 : space.Version * PageMb;
|
||||||
var generators = versionMb >= versionStoreMegabytes / 2.0
|
IReadOnlyList<VersionGenerator> generators = [];
|
||||||
? await Db.QueryAsync(connection, GeneratorsSql, r => new VersionGenerator(r.NullableString(0) ?? "?", (r.NullableLong(1) ?? 0) / 1024.0 / 1024.0), ct).ConfigureAwait(false)
|
DateTimeOffset? generatorsAt = null;
|
||||||
: [];
|
if (versionMb >= versionStoreMegabytes / 2.0)
|
||||||
|
{
|
||||||
|
// Scanning a large version store every 5 minutes is not cheap: at most once per GeneratorsInterval, and a
|
||||||
|
// scan that fails (times out) keeps the previous answer instead of failing the whole collection.
|
||||||
|
if (previous is { VersionGeneratorsAt: { } at } && context.Now - at < GeneratorsInterval)
|
||||||
|
{
|
||||||
|
(generators, generatorsAt) = (previous.VersionGenerators, at);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
generators = await Db.QueryAsync(connection, GeneratorsSql, r => new VersionGenerator(r.NullableString(0) ?? "?", (r.NullableLong(1) ?? 0) / 1024.0 / 1024.0), ct)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
generatorsAt = context.Now;
|
||||||
|
}
|
||||||
|
catch (System.Data.Common.DbException)
|
||||||
|
{
|
||||||
|
(generators, generatorsAt) = (previous?.VersionGenerators ?? [], previous?.VersionGeneratorsAt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return TempdbAnalysis.Advance(previous, new TempdbSnapshot
|
return TempdbAnalysis.Advance(previous, new TempdbSnapshot
|
||||||
{
|
{
|
||||||
|
|
@ -84,6 +108,7 @@ public sealed class TempdbCollector(int versionStoreMegabytes = 1024, int sessio
|
||||||
TopSessions = sessions,
|
TopSessions = sessions,
|
||||||
OldestSnapshotTransaction = oldest,
|
OldestSnapshotTransaction = oldest,
|
||||||
VersionGenerators = generators,
|
VersionGenerators = generators,
|
||||||
|
VersionGeneratorsAt = generatorsAt,
|
||||||
VersionStoreThresholdMegabytes = versionStoreMegabytes,
|
VersionStoreThresholdMegabytes = versionStoreMegabytes,
|
||||||
SessionThresholdMegabytes = sessionMegabytes,
|
SessionThresholdMegabytes = sessionMegabytes,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,9 @@ public sealed record TempdbSnapshot
|
||||||
public SnapshotTransaction? OldestSnapshotTransaction { get; init; }
|
public SnapshotTransaction? OldestSnapshotTransaction { get; init; }
|
||||||
public IReadOnlyList<VersionGenerator> VersionGenerators { get; init; } = [];
|
public IReadOnlyList<VersionGenerator> VersionGenerators { get; init; } = [];
|
||||||
|
|
||||||
|
/// <summary>When <see cref="VersionGenerators"/> was read (the scan is expensive and is repeated at most hourly).</summary>
|
||||||
|
public DateTimeOffset? VersionGeneratorsAt { get; init; }
|
||||||
|
|
||||||
/// <summary>Version store sizes of the latest samples (MB), oldest first: the trend.</summary>
|
/// <summary>Version store sizes of the latest samples (MB), oldest first: the trend.</summary>
|
||||||
public IReadOnlyList<double> VersionStoreHistory { get; init; } = [];
|
public IReadOnlyList<double> VersionStoreHistory { get; init; } = [];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue