namespace ZB.MOM.WW.ScadaBridge.SiteCallAudit; /// /// Configuration options for the Site Call Audit: stuck-call detection + /// KPI windowing for the read-side, plus the cadence/retention knobs for the /// two central-singleton schedulers — the periodic per-site reconciliation /// pull (self-heal for lost telemetry) and the daily terminal-row purge. /// Mirrors the KPI-relevant subset of NotificationOutboxOptions and the /// scheduler-cadence shape of SiteAuditReconciliationOptions / /// AuditLogPurgeOptions. /// public class SiteCallAuditOptions { /// /// Age past which a non-terminal cached call (Pending/Retrying) /// is considered stuck. Display-only — surfaced as the Stuck KPI and a row /// badge, with no escalation. Default 10 minutes, matching /// NotificationOutboxOptions.StuckAgeThreshold. /// public TimeSpan StuckAgeThreshold { get; set; } = TimeSpan.FromMinutes(10); /// /// Trailing window used to compute the delivered- and failed-last-interval /// throughput KPIs. Default 1 minute, matching /// NotificationOutboxOptions.DeliveredKpiWindow. /// public TimeSpan KpiInterval { get; set; } = TimeSpan.FromMinutes(1); /// /// Ask timeout for the central→site Retry/Discard relay. When /// the owning site does not ack a RetryParkedOperation / /// DiscardParkedOperation within this window — site offline, no /// route to the site, or central buffering deliberately absent — the relay /// reports a SiteUnreachable outcome. Default 10 seconds: long enough /// to absorb a healthy cross-cluster round-trip, short enough that an /// operator clicking Retry on an offline site gets a fast, honest answer. /// /// Ordering invariant: RelayTimeout must stay below /// CommunicationOptions.QueryTimeout (default 30s), the timeout the /// outer CommunicationService.RetrySiteCallAsync/DiscardSiteCallAsync /// Ask of the SiteCallAuditActor uses. The outer Ask must outlive this /// inner site relay Ask so the inner relay times out first and yields the /// distinct SiteUnreachable outcome; if the outer Ask expired first, /// that outcome would be lost to a generic Ask-timeout exception. The /// defaults (10s < 30s) satisfy this — keep the gap when tuning either. /// /// public TimeSpan RelayTimeout { get; set; } = TimeSpan.FromSeconds(10); // ── Reconciliation tick: periodic per-site self-heal pull ── /// /// Period of the reconciliation tick. Each tick visits every known site /// once, pulls changed SiteCall rows since a per-site cursor, and /// upserts them idempotently — the documented self-heal when best-effort /// push telemetry is lost. Default 5 minutes, matching the sibling /// SiteAuditReconciliationOptions cadence. Clamped to at least /// via . /// public TimeSpan ReconciliationInterval { get; set; } = TimeSpan.FromMinutes(5); /// /// Test-only override for the reconciliation tick cadence — bypasses the /// clamp so unit tests can drop the /// cadence to milliseconds. Production config never sets this; leave null. /// public TimeSpan? ReconciliationIntervalOverride { get; set; } /// /// Maximum number of SiteCall rows requested per PullSiteCalls /// RPC. Default 500. A MoreAvailable=true response signals the cursor /// advanced and the next tick should keep draining the backlog. /// public int ReconciliationBatchSize { get; set; } = 500; /// /// Minimum interval the config-bound can /// resolve to. Clamps a misconfigured 0 (or negative) value away from /// , which would make Akka's /// ScheduleTellRepeatedlyCancelable spin — the exact footgun flagged in /// a prior review of the sibling reconciliation options. /// private static readonly TimeSpan MinReconciliationInterval = TimeSpan.FromSeconds(1); /// /// Resolves the effective reconciliation tick interval: the test override /// when set (bypassing the clamp), otherwise /// clamped to at least so a /// zero/negative config value can never yield . /// public TimeSpan ResolvedReconciliationInterval => ReconciliationIntervalOverride is { } o ? o : ReconciliationInterval < MinReconciliationInterval ? MinReconciliationInterval : ReconciliationInterval; // ── Purge scheduler: daily terminal-row purge ── /// /// Period of the purge tick. Each tick drops terminal SiteCalls rows /// older than the retention window via /// . /// Default 24 hours, matching AuditLogPurgeOptions. Clamped to at /// least via . /// public TimeSpan PurgeInterval { get; set; } = TimeSpan.FromHours(24); /// /// Test-only override for the purge tick cadence — bypasses the /// clamp so unit tests can drop the cadence /// to milliseconds. Production config never sets this; leave null. /// public TimeSpan? PurgeIntervalOverride { get; set; } /// /// Retention window for terminal rows. On each purge tick a row whose /// TerminalAtUtc is older than UtcNow - RetentionDays is /// deleted; non-terminal rows are never purged. Default 365 days, matching /// the central audit-store retention policy. /// public int RetentionDays { get; set; } = 365; /// /// Minimum interval the config-bound can resolve /// to. Clamps a misconfigured 0 (or negative) value away from /// for the same scheduler-spin reason as /// ; the purge is daily so the floor /// is a more generous 1 minute. /// private static readonly TimeSpan MinPurgeInterval = TimeSpan.FromMinutes(1); /// /// Resolves the effective purge tick interval: the test override when set /// (bypassing the clamp), otherwise clamped to at /// least . /// public TimeSpan ResolvedPurgeInterval => PurgeIntervalOverride is { } o ? o : PurgeInterval < MinPurgeInterval ? MinPurgeInterval : PurgeInterval; }