namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central; /// /// Tuning knobs for the central singleton. /// Defaults mirror the M6 Bundle B brief: pull every 5 minutes per site, 256 rows per /// batch, declare a site "stalled" after two consecutive pull cycles return non-empty /// AND MoreAvailable=true (the backlog is not draining). /// /// /// /// Per the M6 plan the reconciliation actor is the fallback when push telemetry is /// lost; it is intentionally low-frequency. Lowering /// in production trades MS SQL load for /// fresher self-healing — keep the default unless a deployment can prove the extra /// load is acceptable. /// /// /// = 2 because a single non-draining /// cycle can happen on a surge (e.g. a backed-up site replays its hot queue); the /// stalled signal should only fire when the backlog persists across cycles, which is /// the symptom the central health surface is asking us to detect. /// /// public sealed class SiteAuditReconciliationOptions { /// /// Period of the reconciliation tick. Each tick visits every known site once. /// public int ReconciliationIntervalSeconds { get; set; } = 300; /// /// Test-only override for finer control over the tick cadence than /// whole-second resolution allows. When non-null, takes precedence over /// AND bypasses the /// minimum clamp (so tests can use /// millisecond cadences). Production config exposes /// only and never sets this /// knob — but because the options class is Bind-ed wholesale, a /// config value at AuditLog:Reconciliation:ReconciliationIntervalOverride /// WOULD bind if present; operators must not set it. /// public TimeSpan? ReconciliationIntervalOverride { get; set; } /// /// Minimum interval the config-bound /// can resolve to. Clamps a misconfigured ReconciliationIntervalSeconds: 0 /// (or a negative value) away from , which would make /// Akka's ScheduleTellRepeatedlyCancelable spin. The test-only /// bypasses this clamp so unit tests /// can still drop the cadence to milliseconds. /// private static readonly TimeSpan MinConfiguredInterval = TimeSpan.FromSeconds(1); /// /// Resolves the effective tick interval, honouring the test override when /// set. Falls back to , clamped to at /// least so a zero/negative config value can /// never yield (which would spin the scheduler). /// public TimeSpan ReconciliationInterval { get { if (ReconciliationIntervalOverride is { } overrideValue) { return overrideValue; } var resolved = TimeSpan.FromSeconds(ReconciliationIntervalSeconds); return resolved < MinConfiguredInterval ? MinConfiguredInterval : resolved; } } /// /// Maximum number of /// rows requested in a single PullAuditEvents RPC call. /// public int BatchSize { get; set; } = 256; /// /// Number of consecutive non-draining cycles (events returned AND /// MoreAvailable=true) that must accumulate for a site before the actor /// publishes SiteAuditTelemetryStalledChanged(Stalled: true) on the /// EventStream. /// public int StalledAfterNonDrainingCycles { get; set; } = 2; }