-- SyncApplyRunLog: per-invocation, per-target-table audit of what usp_SyncApply -- actually did. This closes the biggest observability gap in the pipeline: the -- apply phase used to be a black box ("apply done") -- queue rows could flip -- to 'error' or 'dead' with no trace in the service log, and there was no -- record of how many rows a MERGE/DELETE touched at any point in time. With -- this table, "what did apply do to table X around time T, and did it fail?" -- is a single indexed query, and CycleID joins each row back to the exact -- [cyc:xxxxxxxx] lines in the Python service log. -- -- Written by usp_SyncApply (sql/02_sync_apply.sql) as a best-effort insert per -- (invocation, target table). Idle cycles write nothing (the proc's cursor -- only visits tables that have pending rows), so growth tracks real change -- traffic, not poll frequency. Retention: unmanaged by default; if it ever -- grows large, purge by StartedAt, e.g. -- DELETE FROM ProductionDataBaseSync.SyncApplyRunLog -- WHERE StartedAt < DATEADD(day, -90, SYSDATETIME()); -- -- Lives under the ProductionDataBaseSync schema (run 00_schema.sql first). -- Idempotent: safe to re-run. Deploy alongside the updated 02_sync_apply.sql; -- ordering is forgiving either way (the proc checks for this table and skips -- the audit insert when it is absent). IF OBJECT_ID('ProductionDataBaseSync.SyncApplyRunLog', 'U') IS NULL BEGIN CREATE TABLE ProductionDataBaseSync.SyncApplyRunLog ( RunLogID bigint IDENTITY(1,1) NOT NULL, CycleID nvarchar(40) NULL, -- correlation id from the Python service ([cyc:...]) TargetSchema nvarchar(128) NOT NULL, TargetTable nvarchar(255) NOT NULL, PendingCount int NOT NULL, -- pending queue rows seen for this table DistinctRecords int NULL, -- distinct RecordIDs among them MergedCount int NULL, -- rows affected by the MERGE (insert + update) DeletedCount int NULL, -- rows affected by the DELETE AppliedCount int NULL, -- queue rows flipped to 'applied' ErrorCount int NULL, -- queue rows flipped to 'error' (will retry) DeadCount int NULL, -- queue rows flipped to 'dead' (retries exhausted) Outcome varchar(10) NOT NULL, -- 'ok' | 'error' ErrorMsg nvarchar(max) NULL, StartedAt datetime2 NOT NULL CONSTRAINT DF_SyncApplyRunLog_Started DEFAULT sysdatetime(), DurationMs int NULL, CONSTRAINT PK_SyncApplyRunLog PRIMARY KEY CLUSTERED (RunLogID) ); END GO -- Join back to the Python service log of one cycle. IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_SyncApplyRunLog_Cycle' AND object_id = OBJECT_ID('ProductionDataBaseSync.SyncApplyRunLog')) BEGIN CREATE INDEX IX_SyncApplyRunLog_Cycle ON ProductionDataBaseSync.SyncApplyRunLog(CycleID); END GO -- "What happened to this table around time T?" IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_SyncApplyRunLog_Table' AND object_id = OBJECT_ID('ProductionDataBaseSync.SyncApplyRunLog')) BEGIN CREATE INDEX IX_SyncApplyRunLog_Table ON ProductionDataBaseSync.SyncApplyRunLog(TargetSchema, TargetTable, StartedAt); END GO -- Fast scan for failed applies. IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_SyncApplyRunLog_Outcome' AND object_id = OBJECT_ID('ProductionDataBaseSync.SyncApplyRunLog')) BEGIN CREATE INDEX IX_SyncApplyRunLog_Outcome ON ProductionDataBaseSync.SyncApplyRunLog(Outcome, StartedAt); END GO