fix(auditlog): explicit permanent rejections on the ingest ack (IngestAck.rejected_event_ids, field 2) with a dedicated counter; reconciler ignores transient faults for abandonment; persist the served high-water mark; prune only on a non-empty enumeration (review 2026-10-21 06/PERF-1, 06/IMPL-1 adversarial follow-up)

This commit is contained in:
Joseph Doherty
2026-10-22 08:40:42 -04:00
parent 38167bca88
commit b0a08bb63b
23 changed files with 790 additions and 168 deletions
@@ -8,4 +8,13 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
/// The gRPC handler echoes these ids back over the wire as the <c>IngestAck</c>
/// the site uses to flip rows to <c>Forwarded</c>.
/// </summary>
public sealed record IngestAuditEventsReply(IReadOnlyList<Guid> AcceptedEventIds);
public sealed record IngestAuditEventsReply(IReadOnlyList<Guid> AcceptedEventIds)
{
/// <summary>
/// Ids central PERMANENTLY refused — a non-transient per-row insert failure (e.g. an
/// over-width value), never a database outage or an exhausted budget. Additive (review
/// 2026-10-21 06/IMPL-1): the site counts only these toward giving a row up as
/// <c>Rejected</c>; empty from an older central, which therefore never triggers it.
/// </summary>
public IReadOnlyList<Guid> RejectedEventIds { get; init; } = Array.Empty<Guid>();
}
@@ -7,4 +7,13 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
/// UPSERT) committed; entries whose transaction rolled back are absent so the
/// site can leave the row Pending and retry on the next drain.
/// </summary>
public sealed record IngestCachedTelemetryReply(IReadOnlyList<Guid> AcceptedEventIds);
public sealed record IngestCachedTelemetryReply(IReadOnlyList<Guid> AcceptedEventIds)
{
/// <summary>
/// Ids central PERMANENTLY refused — a non-transient per-row insert failure (e.g. an
/// over-width value), never a database outage or an exhausted budget. Additive (review
/// 2026-10-21 06/IMPL-1): the site counts only these toward giving a row up as
/// <c>Rejected</c>; empty from an older central, which therefore never triggers it.
/// </summary>
public IReadOnlyList<Guid> RejectedEventIds { get; init; } = Array.Empty<Guid>();
}