perf(comms): alarms-only seed, capped buffers, at-least-once audit pull

This commit is contained in:
Joseph Doherty
2026-08-14 21:10:19 -04:00
parent 1040dc0fcc
commit 2ce0ad7ed1
30 changed files with 1941 additions and 482 deletions
@@ -109,10 +109,47 @@ public interface ISiteAuditQueue
/// </remarks>
/// <param name="sinceUtc">Lower bound timestamp (UTC).</param>
/// <param name="batchSize">Maximum number of rows to return.</param>
/// <param name="afterId">
/// Composite-keyset tiebreak cursor (WP2.3), mirroring
/// <see cref="IOperationTrackingStore.ReadChangedSinceAsync"/>: when non-null it is the
/// <see cref="AuditEvent.EventId"/> ("D" GUID form) of the last row already consumed at
/// <paramref name="sinceUtc"/>, and only rows strictly after the composite
/// <c>(OccurredAtUtc, EventId)</c> pair are returned — so a burst sharing one exact
/// instant drains via the id tiebreak instead of pinning the inclusive-timestamp cursor.
/// Null (the first pull, or a central that never sets it) keeps the inclusive
/// <c>&gt;=</c> contract.
/// </param>
/// <param name="ct">Cancellation token.</param>
/// <returns>A task that resolves to audit events at or after <paramref name="sinceUtc"/> in pending or forwarded state, up to <paramref name="batchSize"/>.</returns>
Task<IReadOnlyList<AuditEvent>> ReadPendingSinceAsync(
DateTime sinceUtc, int batchSize, CancellationToken ct = default);
DateTime sinceUtc, int batchSize, string? afterId = null, CancellationToken ct = default);
/// <summary>
/// Reconciliation-pull commit surface, cursor form (WP2.3): flips every row at or before
/// the composite cursor <c>(<paramref name="sinceUtc"/>, <paramref name="afterId"/>)</c>
/// from <see cref="ZB.MOM.WW.ScadaBridge.Commons.Types.Enums.AuditForwardState.Pending"/>/
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Types.Enums.AuditForwardState.Forwarded"/> to
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Types.Enums.AuditForwardState.Reconciled"/>.
/// </summary>
/// <remarks>
/// The cursor is central's proof of receipt: it only advances past rows central has
/// actually ingested. Flipping on THAT — rather than on having merely served the rows —
/// is what makes the pull at-least-once: a fault between the response leaving the site
/// and central committing it leaves the rows unflipped, so the next pull re-serves them
/// (central dedups on <see cref="AuditEvent.EventId"/>).
/// <para>
/// With <paramref name="afterId"/> null the cursor is a bare timestamp under the legacy
/// inclusive <c>&gt;=</c> read contract, so only rows STRICTLY older than
/// <paramref name="sinceUtc"/> are proven received; rows at the boundary instant are left
/// alone. Idempotent; already-Reconciled rows are untouched.
/// </para>
/// </remarks>
/// <param name="sinceUtc">The cursor timestamp central has consumed up to (UTC).</param>
/// <param name="afterId">The last consumed <see cref="AuditEvent.EventId"/> at that instant, or null.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>A task that resolves to the number of rows flipped.</returns>
Task<int> MarkReconciledUpToAsync(
DateTime sinceUtc, string? afterId, CancellationToken ct = default);
/// <summary>
/// Reconciliation-pull commit surface: flips the supplied EventIds to