Files
ScadaBridge/src/ScadaLink.AuditLog/Site/IAuditWriteFailureCounter.cs
T
Joseph Doherty ff8766ec8b feat(auditlog): FallbackAuditWriter compose SQLite + ring + failure counter (#23)
Adds the IAuditWriter composer that sits between the script-side
ScriptRuntimeContext audit emission (Bundle F) and the primary
SqliteAuditWriter. Honours the alog.md §7 guarantee that audit-write
failures NEVER abort the user-facing action:

- Primary throw -> log Warning, increment IAuditWriteFailureCounter
  (Bundle G's health-metric sink), stash the event in the drop-oldest
  RingBufferFallback, return success to the caller.
- Primary success -> opportunistically drain the ring back through the
  primary in FIFO order, behind the triggering event. Drain is
  serialised via a SemaphoreSlim gate so concurrent recoveries don't
  double-replay; a drain-side re-throw re-enqueues at the tail and
  breaks out (the next successful write retries).

Adds IAuditWriteFailureCounter as the lightweight DI seam (one void
Increment()), and a TryDequeue helper on RingBufferFallback that the
recovery path uses to pop one item without blocking.

Tests (4 new, total 26 -> 30):
- WriteAsync_PrimaryThrows_EventLandsInRing_CallReturnsSuccess
- WriteAsync_PrimaryRecovers_RingDrains_InFIFOOrder_OnNextWrite
  (order: trigger first, then ring backlog in submission FIFO)
- WriteAsync_PrimaryAlwaysSucceeds_Ring_StaysEmpty
- WriteAsync_FailureCounter_Incremented_Per_PrimaryFailure
2026-05-20 12:23:50 -04:00

15 lines
542 B
C#

namespace ScadaLink.AuditLog.Site;
/// <summary>
/// Lightweight counter sink invoked by <see cref="FallbackAuditWriter"/> every
/// time the primary <see cref="SqliteAuditWriter"/> throws on an audit write.
/// Bundle G (M2-T11) implements this as a thread-safe Interlocked counter
/// bridged into the Site Health Monitoring report payload as
/// <c>SiteAuditWriteFailures</c>.
/// </summary>
public interface IAuditWriteFailureCounter
{
/// <summary>Increment the audit-write failure counter by one.</summary>
void Increment();
}