namespace ZB.MOM.WW.ScadaBridge.Transport.Import; /// /// Register row 52: a sliding window of bundle-unlock attempts that is shared across the /// central pair, so TransportOptions.MaxUnlockAttemptsPerIpPerHour is a FLEET cap /// rather than a per-node, per-process one. /// /// Implementations are expected to THROW on store failure. The fail-open decision belongs /// to , which owns the policy (log a Warning and /// permit the attempt), not to the storage layer. /// /// public interface IBundleUnlockAttemptWindow { /// /// Registers one unlock attempt against the shared window and reports whether it was /// within budget. Implementations must NOT record an attempt they are rejecting — /// a denial burns no budget, matching the in-memory limiter's contract (register /// row 44), so a client that keeps hammering cannot extend its own lockout. /// /// Throttle bucket — the caller's remote IP, or the shared unattributed bucket. /// The trailing-window cap (MaxUnlockAttemptsPerIpPerHour). /// Trailing window the attempts are counted over (one hour in production). /// Cancellation token. /// true when the attempt was recorded (within budget); false when the bucket is exhausted. Task TryRegisterAttemptAsync( string clientKey, int maxAttemptsPerWindow, TimeSpan window, CancellationToken ct = default); /// /// Register row 65: drives one retention sweep of aged-out attempt rows independently /// of the unlock path. The prune used to ride /// only, so a cluster that stopped importing entirely left its last rows in place /// indefinitely; BundleSessionEvictionService — Transport's periodic /// central-only sweep — now also ticks this, which closes that. /// /// Unlike , implementations must NOT throw on /// store failure: this is pure retention maintenance with no caller to report to, and /// the sweep that drives it must never be taken down by a database blip. Implementations /// are also expected to keep their own cadence claim, so calling this more often than /// the intended prune interval is harmless. /// /// /// Trailing window the attempts are counted over (one hour in production); rows are retained for a multiple of it. /// Cancellation token. /// A task that completes when the prune attempt has finished (or been skipped for cadence). Task PruneAsync(TimeSpan window, CancellationToken ct = default); }