perf(audit-log): bound GetExecutionTreeAsync edge scan to a root-anchored time window (partition elimination)
This commit is contained in:
@@ -665,6 +665,16 @@ VALUES
|
||||
// practice.
|
||||
private const int ExecutionChainMaxDepth = 32;
|
||||
|
||||
// Execution trees span minutes, not years — an inbound request and every script it
|
||||
// spawns all fire inside one operational burst. Anchoring the Edges CTE scan to a
|
||||
// narrow window around the root's first event lets SQL Server eliminate every
|
||||
// AuditLog partition outside it, turning a full-table DISTINCT scan into a seek on
|
||||
// IX_AuditLog_Execution over one or two months (arch-review 04, P2). The slack absorbs
|
||||
// minor clock skew before the root; the max span is the documented traversal bound —
|
||||
// a genuine descendant stamped beyond it is excluded by design.
|
||||
private static readonly TimeSpan ExecutionTreeWindowSlack = TimeSpan.FromHours(1);
|
||||
private static readonly TimeSpan ExecutionTreeMaxSpan = TimeSpan.FromDays(7);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ExecutionTreeNode>> GetExecutionTreeAsync(
|
||||
Guid executionId,
|
||||
@@ -713,6 +723,31 @@ VALUES
|
||||
rootExecutionId = parent.Value;
|
||||
}
|
||||
|
||||
// --- Between phases: bound the down-walk to a root-anchored window ---
|
||||
// The root's first event timestamp anchors a [rootFirst - slack, rootFirst + maxSpan)
|
||||
// window used to prune the Edges CTE scan to a handful of partitions. A row-less stub
|
||||
// root (purged/no-action parent) has no MIN — leave the scan unbounded in that
|
||||
// degenerate case (correctness over speed).
|
||||
DateTime? rootFirstOccurred;
|
||||
await using (var winCmd = conn.CreateCommand())
|
||||
{
|
||||
winCmd.CommandText =
|
||||
"SELECT MIN(OccurredAtUtc) FROM dbo.AuditLog WHERE ExecutionId = @root;";
|
||||
var pWinRoot = winCmd.CreateParameter();
|
||||
pWinRoot.ParameterName = "@root";
|
||||
pWinRoot.Value = rootExecutionId;
|
||||
winCmd.Parameters.Add(pWinRoot);
|
||||
|
||||
var winResult = await winCmd.ExecuteScalarAsync(ct).ConfigureAwait(false);
|
||||
rootFirstOccurred = winResult is null or DBNull ? null : (DateTime)winResult;
|
||||
}
|
||||
|
||||
// Build the optional window predicate for the Edges CTE. When the root has rows the
|
||||
// scan is confined to the window (partition elimination); otherwise it stays unbounded.
|
||||
var edgesWindowPredicate = rootFirstOccurred is null
|
||||
? string.Empty
|
||||
: "AND OccurredAtUtc >= @winStart AND OccurredAtUtc < @winEnd";
|
||||
|
||||
// --- Phase 2: walk down from the root via a recursive CTE ---------
|
||||
// Edges : a non-recursive, DISTINCT (ExecutionId, ParentExecutionId)
|
||||
// edge set distilled from AuditLog. Recursing over edges
|
||||
@@ -743,6 +778,7 @@ VALUES
|
||||
SELECT DISTINCT ExecutionId, ParentExecutionId
|
||||
FROM dbo.AuditLog
|
||||
WHERE ExecutionId IS NOT NULL
|
||||
{edgesWindowPredicate}
|
||||
),
|
||||
Chain AS (
|
||||
-- Anchor: the root execution id, seeded as a literal so
|
||||
@@ -797,6 +833,19 @@ VALUES
|
||||
pRoot.Value = rootExecutionId;
|
||||
downCmd.Parameters.Add(pRoot);
|
||||
|
||||
if (rootFirstOccurred is { } rootFirst)
|
||||
{
|
||||
var pWinStart = downCmd.CreateParameter();
|
||||
pWinStart.ParameterName = "@winStart";
|
||||
pWinStart.Value = rootFirst - ExecutionTreeWindowSlack;
|
||||
downCmd.Parameters.Add(pWinStart);
|
||||
|
||||
var pWinEnd = downCmd.CreateParameter();
|
||||
pWinEnd.ParameterName = "@winEnd";
|
||||
pWinEnd.Value = rootFirst + ExecutionTreeMaxSpan;
|
||||
downCmd.Parameters.Add(pWinEnd);
|
||||
}
|
||||
|
||||
await using var reader = await downCmd.ExecuteReaderAsync(ct).ConfigureAwait(false);
|
||||
while (await reader.ReadAsync(ct).ConfigureAwait(false))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user