feat(audit): M5.6 SourceNode sentinel backfill (purge-role) + CLI + runbook note (T5)
This commit is contained in:
@@ -201,4 +201,59 @@ public interface IAuditLogRepository
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the distinct, non-null source node names in ascending order.</returns>
|
||||
Task<IReadOnlyList<string>> GetDistinctSourceNodesAsync(CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// M5.6 (T5) one-time operational backfill: sets <c>SourceNode</c> to
|
||||
/// <paramref name="sentinel"/> on every row where <c>SourceNode IS NULL</c>
|
||||
/// and <c>OccurredAtUtc < <paramref name="before"/></c>, in bounded
|
||||
/// batches of <paramref name="batchSize"/> rows, looping until no further
|
||||
/// rows match. Returns the total number of rows updated across all batches.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <b>Why a sentinel, not the real value.</b> <c>SourceNode</c> captures the
|
||||
/// physical cluster node on which an event was emitted. For pre-feature rows
|
||||
/// that were ingested before the column was stamped, the true node-of-origin
|
||||
/// is UNKNOWABLE — the original emitter is long gone and there is no
|
||||
/// retroactive way to determine it. Backfilling a configurable sentinel
|
||||
/// (default <c>"unknown"</c>) makes it explicit that these rows pre-date the
|
||||
/// feature rather than silently leaving them NULL (which the filter UI already
|
||||
/// treats as "unresolved" but which an operator might mistake for a bug).
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b><c>ExecutionId</c> / <c>ParentExecutionId</c> cannot be backfilled.</b>
|
||||
/// These are PERSISTED COMPUTED columns derived from <c>DetailsJson</c>. The
|
||||
/// AuditLog append-only invariant forbids mutating <c>DetailsJson</c>, so
|
||||
/// the computed values for pre-feature rows remain NULL permanently. This is
|
||||
/// documented rather than coded — see the Ops Note in
|
||||
/// <c>Component-AuditLog.md § Ops Notes — Historical Null Columns</c>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Maintenance path — NOT the writer role.</b> This UPDATE runs on the
|
||||
/// purge/maintenance connection (the same path as
|
||||
/// <see cref="SwitchOutPartitionAsync"/> and any per-channel purge), NOT the
|
||||
/// append-only <c>scadabridge_audit_writer</c> role. The CI guard
|
||||
/// (<c>AuditLogAppendOnlyGuardTests</c>) recognises the
|
||||
/// <c>// AUDIT-PURGE-ALLOWED</c> marker on the UPDATE line and forgives
|
||||
/// exactly this one sanctioned maintenance-path UPDATE; any other UPDATE
|
||||
/// against <c>AuditLog</c> still trips the guard.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Bounded + idempotent.</b> <c>UPDATE TOP (@batch)</c> caps the
|
||||
/// transaction-log and lock footprint per statement. The loop exits when a
|
||||
/// batch updates zero rows, so a crash mid-loop is recoverable by simply
|
||||
/// running again; re-running after completion is a no-op (no NULL rows
|
||||
/// remain for the given <paramref name="before"/> window).
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="sentinel">Value to write into <c>SourceNode</c> for pre-feature rows (e.g. <c>"unknown"</c>).</param>
|
||||
/// <param name="before">Rows with <c>OccurredAtUtc</c> strictly older than this UTC datetime are eligible.</param>
|
||||
/// <param name="batchSize">Maximum rows updated per batch; must be > 0.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the total number of rows updated across all batches.</returns>
|
||||
Task<long> BackfillSourceNodeAsync(
|
||||
string sentinel,
|
||||
DateTime before,
|
||||
int batchSize,
|
||||
CancellationToken ct = default);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user