Files
ScadaBridge/src/ZB.MOM.WW.ScadaBridge.AuditLog/Site/SqliteAuditWriterOptions.cs
T

72 lines
3.9 KiB
C#

namespace ZB.MOM.WW.ScadaBridge.AuditLog.Site;
/// <summary>
/// Options for the site-side SQLite hot-path audit writer.
/// Mirrors the ZB.MOM.WW.ScadaBridge.SiteEventLogging pattern: a single SQLite connection
/// fed by a background writer task draining a bounded
/// <see cref="System.Threading.Channels.Channel{T}"/> so script-thread enqueues
/// never block on disk I/O.
/// </summary>
public sealed class SqliteAuditWriterOptions
{
/// <summary>
/// SQLite database path (or in-memory URI for tests). No default —
/// mirrors <c>LocalDb:Path</c> (<c>ZB.MOM.WW.LocalDb.LocalDbOptions.Path</c>): a
/// bare filename here would resolve relative to the process CWD, which on the
/// docker rig is the container's ephemeral overlayfs, not the mounted
/// <c>/app/data</c> volume — the file (and its pending forward-state backlog)
/// would be silently discarded on every container recreate. Site nodes MUST set
/// this explicitly (e.g. <c>/app/data/auditlog.db</c>); <c>StartupValidator</c>
/// in ZB.MOM.WW.ScadaBridge.Host enforces the requirement pre-host for Site nodes only,
/// the same way it enforces <c>Communication:GrpcPsk</c>. Central composition roots also
/// bind this options type (<c>AddAuditLog</c> is shared between roles) but never resolve
/// <see cref="SqliteAuditWriter"/>, so the empty default is inert there and deliberately
/// left unvalidated by <see cref="SqliteAuditWriterOptionsValidator"/> (which only checks
/// the role-agnostic channel/batch/flush knobs) — enforcing it there too would fail
/// central's boot via <c>ValidateOnStart</c>, since <c>AddAuditLog</c> runs on both roles.
/// </summary>
public string DatabasePath { get; set; } = "";
/// <summary>
/// Capacity of the bounded write queue. Set high enough that ordinary
/// script bursts never fill it. When the writer falls behind and the queue is
/// full, <c>WriteAsync</c> fails fast with <see cref="SqliteAuditWriterSaturatedException"/>
/// instead of parking an unbounded number of waiters (review 2026-10-21 06/MEM-1);
/// <see cref="FallbackAuditWriter"/> turns that into its documented best-effort
/// drop-oldest ring + <c>SiteAuditWriteFailures</c> counter bump.
/// </summary>
public int ChannelCapacity { get; set; } = 4096;
/// <summary>Max number of pending events the writer drains in one transaction.</summary>
public int BatchSize { get; set; } = 256;
/// <summary>Soft flush interval the writer enforces when fewer than BatchSize events are queued.</summary>
public int FlushIntervalMs { get; set; } = 50;
/// <summary>
/// Maximum rows the retention purge deletes in ONE transaction. The purge loops page by
/// page and releases the write lock between pages, so the batched hot-path writer
/// interleaves with a large purge instead of stalling behind one multi-minute DELETE
/// (review 2026-10-21 06/MEM-1 — the same hazard class register row 34 fixed for
/// <c>site_events</c>).
/// </summary>
public int PurgeBatchSize { get; set; } = 5000;
/// <summary>
/// Free pages reclaimed by the bounded <c>PRAGMA incremental_vacuum(N)</c> run after each
/// purge page. Bounds the time the vacuum holds the write lock; pages not reclaimed in
/// one step stay on the freelist and are reused by later inserts or reclaimed by the
/// next purge.
/// </summary>
public int PurgeVacuumPagesPerBatch { get; set; } = 1000;
/// <summary>
/// Poll cadence, in seconds, for the <c>SiteAuditBacklogReporter</c> hosted service
/// that probes the SQLite audit backlog and pushes the snapshot onto the site health
/// report. Half a typical 60 s health-report interval keeps the snapshot fresh without
/// spinning the SQL probe more often than necessary. A non-positive value falls back to
/// the reporter's 30 s default.
/// </summary>
public int BacklogPollIntervalSeconds { get; set; } = 30;
}