Files
ScadaBridge/docs/plans/sql/AddSiteCallsListSortIndex.sql
Joseph Doherty eca0ab768d fix(auditlog)+fix(configdb): budget the ingest fallback loop; index + varchar-type the SiteCalls list query (arch-review 2026-08-19 P5-1 + P5-2)
P5-1: AuditLogIngestActor.OnCachedTelemetryAsync's per-entry fallback loop
(used when the cached-telemetry batch fast path fails) ran with no
budget/cancellation, unlike OnIngestAsync's fallback. A slow/wedged DB could
freeze the central ingest singleton's mailbox for minutes. The loop now runs
under its own fresh IngestFallbackBudget CancellationTokenSource (mirroring
OnIngestAsync), passes the token into BeginTransactionAsync/
InsertIfNotExistsAsync/UpsertAsync, and stops early on budget exhaustion,
logging how many entries were left for the periodic reconciliation pull to
pick up.

P5-2: SiteCallAuditRepository.QueryAsync ordered by
(CreatedAtUtc DESC, TrackedOperationId DESC) with no index leading
unconditionally on CreatedAtUtc, forcing a full scan+sort on an unfiltered
page. Added IX_SiteCalls_Created_TrackedOperationId (migration
AddSiteCallsListSortIndex + matching hand-written production-parity script
under docs/plans/sql/). Also switched the query from FromSqlInterpolated
(which binds string filters as nvarchar(4000)) to FromSqlRaw with explicitly
SqlDbType.VarChar-typed parameters, matching the SiteCalls columns'
IsUnicode(false) declaration and the write path's existing Param(...)
pattern, so seeks — including the keyset predicate — are no longer blocked
by an implicit conversion.

Tests: new AuditLogIngestActorCombinedTelemetryTests case proves the
fallback budget bounds the wait (hanging repo stub, ~5s not indefinite),
that remaining entries are skipped, and that a non-default/cancellable
token reaches the repository call; verified fail-unfixed by reverting the
actor change (test times out at its 20s bound). Existing
SiteCallAuditRepositoryTests QueryAsync/keyset/stuck-cutoff cases (real
MSSQL) confirm the raw-SQL rewrite still returns correct pages.
2026-08-19 14:49:24 -04:00

51 lines
2.2 KiB
Transact-SQL

-- arch-review 2026-08-19 P5-2 (register row-35 class): the SiteCalls list page's
-- sort key gets its own index.
--
-- SiteCallAuditRepository.QueryAsync orders (CreatedAtUtc DESC, TrackedOperationId
-- DESC), and its keyset predicate is exactly that pair. None of the existing
-- SiteCalls indexes lead unconditionally on CreatedAtUtc:
-- - IX_SiteCalls_Source_Created requires an equality on SourceSite first.
-- - IX_SiteCalls_NonTerminal / IX_SiteCalls_Terminal are each filtered to one
-- side of TerminalAtUtc.
-- So an unfiltered (or lightly filtered) page fell back to a full clustered-index
-- scan + sort. IX_SiteCalls_Created_TrackedOperationId (CreatedAtUtc DESC,
-- TrackedOperationId DESC) makes a forward scan of the index deliver rows in the
-- requested order directly, so a keyset page reads a Top-N off the index instead
-- of sorting the whole table on every page request.
--
-- SiteCalls is small enough today that a plain (offline) index build is fine. On
-- an Enterprise edition instance where the table has grown large and the Central
-- UI must stay responsive, add WITH (ONLINE = ON) to the CREATE INDEX below.
--
-- Run with QUOTED_IDENTIFIER ON. Several tables in this database carry filtered
-- indexes and persisted computed columns, and SQL Server refuses DDL/DML against
-- them with error 1934 when the session has QUOTED_IDENTIFIER OFF -- which is
-- exactly what `sqlcmd` gives you by default (pass -I, or keep this header). The
-- SETs sit in their own batch so they are in force when the statements after the
-- GO parse.
SET QUOTED_IDENTIFIER ON;
SET ANSI_NULLS ON;
GO
BEGIN TRANSACTION;
IF NOT EXISTS (
SELECT * FROM [__EFMigrationsHistory]
WHERE [MigrationId] = N'20260819183326_AddSiteCallsListSortIndex'
)
BEGIN
EXEC(N'CREATE INDEX [IX_SiteCalls_Created_TrackedOperationId]
ON [SiteCalls] ([CreatedAtUtc] DESC, [TrackedOperationId] DESC)');
END;
IF NOT EXISTS (
SELECT * FROM [__EFMigrationsHistory]
WHERE [MigrationId] = N'20260819183326_AddSiteCallsListSortIndex'
)
BEGIN
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
VALUES (N'20260819183326_AddSiteCallsListSortIndex', N'10.0.7');
END;
COMMIT;
GO