using Deal.Modules.Settings.Application.Abstractions; using Deal.Modules.Settings.Application.Models; using Deal.Modules.Settings.Application.Registrars; using Deal.Modules.Settings.Application.Services; using Deal.Modules.Discovery.Application.Abstractions; using Deal.Modules.Discovery.Application.Exceptions; using Deal.Modules.Discovery.Application.Extensions; using Deal.Modules.Discovery.Application.Models; using Deal.Modules.Discovery.Application.Registrars; namespace Deal.Modules.Discovery.Application.Services; /// /// Продовая реализация : случайная пауза из настроек тенанта (ban_guard L44–56). /// /// /// Защита инварианта min ≤ max как в python L47–51: настройки мог изменить один конец интервала (single-key /// PATCH), поэтому при инверсии концы меняются местами. Обе настройки не заданы/≤0 — паузы нет (крайний случай /// L52–53). Детерминизма нет: интервал сэмплируется из . /// public sealed class DiscoveryPacer(ISettingsStore settings) : IDiscoveryPacer { /// public async Task WaitJoinDelayAsync(CancellationToken ct) { TenantSettingsSnapshot snapshot = await TenantSettingsSnapshot.LoadAsync(settings, ct); int low = snapshot.GetInt(SettingsKeys.DiscJoinDelayMin, SettingsDefaults.DiscJoinDelayMin); int high = snapshot.GetInt(SettingsKeys.DiscJoinDelayMax, SettingsDefaults.DiscJoinDelayMax); if (low <= 0 && high <= 0) { return; } if (low > high) { (low, high) = (high, low); } double seconds = low + Random.Shared.NextDouble() * (high - low); await Task.Delay(TimeSpan.FromSeconds(seconds), ct).ConfigureAwait(false); } }