From 87ddb22369636f9c86ffb7628a62e2fdace0b262 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 13 Aug 2026 22:02:56 -0700 Subject: [PATCH] perf: raise the auto-denylist cap to 324,449 to cover observed floods Distinct-source floods of 50k-250k machines are seen in practice, which overflows a 156,437 cap. Past the cap Hold() refuses to track, so those addresses are still disconnected by whichever gate caught them but pay full detection cost on every reconnect instead of a cheap accept-gate deny -- the expensive path during exactly the event the list exists for. 324,449 is the next Dictionary capacity in the from-empty progression (36,353 -> 75,431 -> 156,437 -> 324,449), so it fills one exactly. ~17 MB. The hot-path cost barely moves: cap-triggered sweeps are throttled to one a second, so a flood pays 0.65ms/sec, 0.065% of the loop. Only the flood-end sweep grows, from ~2ms to ~6ms, once. Co-Authored-By: Claude Opus 5 (1M context) --- .../Network/AutoDenylist/AutoDenylistConfiguration.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Projects/UOContent/Network/AutoDenylist/AutoDenylistConfiguration.cs b/Projects/UOContent/Network/AutoDenylist/AutoDenylistConfiguration.cs index 3af1a300b..cffadc48a 100644 --- a/Projects/UOContent/Network/AutoDenylist/AutoDenylistConfiguration.cs +++ b/Projects/UOContent/Network/AutoDenylist/AutoDenylistConfiguration.cs @@ -76,11 +76,12 @@ public record AutoDenylistSettings /// disconnected by whichever gate detected them. /// /// - /// A Dictionary capacity, not a round number. Growing from empty it steps 36,353 → 75,431 → - /// 156,437, so this fills one exactly instead of stranding slots: 65,536 sat just past a resize and left - /// 9,895 of them (52 bytes each) unusable. Costs ~8 MB. Raising it is paid on the loop, where a full - /// sweep runs ~2 ms here, ~6 ms at 324,449 and ~26 ms at 968,897. + /// A Dictionary capacity, not a round number. Grown from empty it steps 36,353 → 75,431 → + /// 156,437 → 324,449, so this fills one exactly instead of stranding slots: 65,536 sat just past a + /// resize and left 9,895 of them (52 bytes each) unusable. Sized to cover the 50k–250k distinct-source + /// floods seen in practice, for ~17 MB. Raising it is paid on the loop, where a full sweep runs ~6 ms + /// here and ~26 ms at 968,897. /// [JsonPropertyName("maxEntries")] - public int MaxEntries { get; set; } = 156_437; + public int MaxEntries { get; set; } = 324_449; }