namespace ZB.MOM.WW.ScadaBridge.AuditLog.Site; /// /// 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 /// so script-thread enqueues /// never block on disk I/O. /// public sealed class SqliteAuditWriterOptions { /// /// SQLite database path (or in-memory URI for tests). No default — /// mirrors LocalDb:Path (ZB.MOM.WW.LocalDb.LocalDbOptions.Path): 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 /// /app/data 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. /app/data/auditlog.db); StartupValidator /// in ZB.MOM.WW.ScadaBridge.Host enforces the requirement pre-host for Site nodes only, /// the same way it enforces Communication:GrpcPsk. Central composition roots also /// bind this options type (AddAuditLog is shared between roles) but never resolve /// , so the empty default is inert there and deliberately /// left unvalidated by (which only checks /// the role-agnostic channel/batch/flush knobs) — enforcing it there too would fail /// central's boot via ValidateOnStart, since AddAuditLog runs on both roles. /// public string DatabasePath { get; set; } = ""; /// /// 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, WriteAsync fails fast with /// instead of parking an unbounded number of waiters (review 2026-10-21 06/MEM-1); /// turns that into its documented best-effort /// drop-oldest ring + SiteAuditWriteFailures counter bump. /// public int ChannelCapacity { get; set; } = 4096; /// Max number of pending events the writer drains in one transaction. public int BatchSize { get; set; } = 256; /// Soft flush interval the writer enforces when fewer than BatchSize events are queued. public int FlushIntervalMs { get; set; } = 50; /// /// 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 /// site_events). /// public int PurgeBatchSize { get; set; } = 5000; /// /// Free pages reclaimed by the bounded PRAGMA incremental_vacuum(N) 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. /// public int PurgeVacuumPagesPerBatch { get; set; } = 1000; /// /// Poll cadence, in seconds, for the SiteAuditBacklogReporter 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. /// public int BacklogPollIntervalSeconds { get; set; } = 30; }